java面向对象 final && static 关键字

84 阅读14分钟

java面向对象 final && static 关键字

目录

[TOC]

关键字:static

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下, 某些特定的数据在内存空间里只有一份 ,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量

class Circle{ private double radius; public Circle(double radius){this.radius=radius;} public double findArea(){return Math.PIradiusradius;}} 创建两个Circle对象 Circle c1=new Circle(2.0); //c1.radius=2.0 Circle c2=new Circle(3.0); //c2.radius=3.0

Circle类中的变量radius是一个实例变量(instance variable),它属于类的每一个对象,不能被同一个类的不同对象所共享。

上例中c1radius独立于c2radius,存储在不同的空间。c1中的radius 变化不会影响c2radius,反之亦然。

如果想让一个类的所有实例共享数据,就用类变量!

类属性、类方法的设计思想

类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应 的方法设置为类方法。

如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

使用范围: 在Java类中,可用static修饰属性、方法代码块、内部类

修饰后的成员具备以下特点

  • 随着类的加载而加载
  • 优先于对象存在
  • 修饰的成员,被所有对象所共享
  • 访问权限允许时,可不创建对象,直接被类调用

类变量(class Variable)

类变量(类属性)由该类的所有实例共享

public class Person {
    private int id;
    public static int total = 0;
    public Person() {
    total++;
    id = total;
    }
}
public static void main(String args[]){
    Person Tom=new Person();
    Tom.id=0;
    total=100; // 不用创建对象就可以访问静态成员
    }
}
public class StaticDemo {
    public static void main(String args[]) {
    Person.total = 100; // 不用创建对象就可以访问静态成员
    //访问方式:类名.类属性,类名.类方法
    System.out.println(Person.total);
    Person c = new Person();
    System.out.println(c.total); //输出101
}}

静态变量的内存解析

类方法(class method)

  • 没有对象的实例时,可以用 类名 . 方法名 () 的形式访问由static修饰的类方法。
  • static 方法内部只能访问类的 static 修饰的属性或方法,不能访问类的非 static 的结构。
class Person {
    private int id;
    private static int total = 0;
    public static int getTotalPerson() { 
        //id++; //非法
        return total;}
    public Person() {
        total++;
        id = total;
    }}
public class PersonTest {
    public static void main(String[] args) {
    System.out.println("Number of total is " + Person.getTotalPerson());
    //没有创建对象也可以访问静态方法
    Person p1 = new Person();
    System.out.println( "Number of total is "+ Person.getTotalPerson());
}}

因为不需要实例就可以访问 static 方法,因此 static 方法内部不能有 this ( 不能有 super ? YES!) static 修饰的方法不能被重写

class Person {
    private int id;
    private static int total = 0;
    public static void setTotalPerson(int total){
        this.total=total; //非法,在static方法中不能有this,也不能有super
    }
public Person() {
    total++;
    id = total;
    }}
public class PersonTest {
    public static void main(String[] args) {
        Person.setTotalPerson(3);
} }

单例 (Singleton)设计模式

设计模式 是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典 的棋谱,不同的棋局,我们用不同的棋谱。套路

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类 只能存在一个对象实例 ,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,

我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的

饿汉式

class Singleton {
    // 1.私有化构造器
    private Singleton() {
    }
    // 2.内部提供一个当前类的实例
    // 4.此实例也必须静态化
private static Singleton single = new Singleton();
    // 3.提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
    return single;
    }
}

懒汉式

class Singleton {
// 1.私有化构造器
    private Singleton() {
    }
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
    // 3.提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
        if(single == null) {
            single = new Singleton();
        }
    return single;
    }
}

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的 产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可 以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

- 应用场景

  • 网站的计数器,一般也是单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库 资源。
  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
  • Application也是单例的典型应用
  • WindowsTask Manager (任务管理器)就是很典型的单例模式
  • WindowsRecycle Bin (回收站)也是典型的单例应用。在整个系统运行过程 中,回收站一直维护着仅有的一个实例

理解main方法的语法

由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是 public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须 是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。

又因为main()方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情 况,我们在之前的例子中多次碰到

public class CommandPara {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "] = " + args[i]);
        }
    }
}

//运行程序CommandPara.java

java CommandPara “Tom" “Jerry" “Shkstart"

代码块

码块(或初始化块)的作用: Java 类或对象进行初始化

代码块(或初始化块)的分类:

  • 一个类中代码块若有修饰符,则只能被static修饰,称为 静态代码块 (static block),
  • 没有使用static修饰的,为 非静态代码块

static 代码块通常用于初始化 static 的属性

class Person {
    public static int total;
    static {
        total = 100;//为total赋初值
    }
    …… //其它属性或方法声明
}

静态代码块:用 static 修饰的代码块

  • 1.可以有输出语句。
  • 2.可以对类的属性、类的声明进行初始化操作。
  • 3.不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  • 4.若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  • 5.静态代码块的执行要先于非静态代码块。
  • 6.静态代码块随着类的加载而加载,且只执行一次。

非静态代码块:没有 static 修饰的代码块

  • 1.可以有输出语句。
  • 2.可以对类的属性、类的声明进行初始化操作。
  • 3.除了调用非静态的结构外,还可以调用静态的变量或方法。
  • 4.若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  • 5.每次创建对象的时候,都会执行一次。且先于构造器执行。

静态初始化块举例

class Person {
    public static int total;
    static {
        total = 100;
        System.out.println("in static block!");
    }
}
public class PersonTest {
    public static void main(String[] args) {
        System.out.println("total = " + Person.total);
        System.out.println("total = " + Person.total);
    }
}
//in static block
//total=100
//total=100

程序中成员变量赋值的执行顺序

  • 声明成员变量的默认初始化
  • 显式初始化、多个初始化块依次被执行
  • \构造器再对成员进行初始化操作(同级别下按先后顺序执行)
  • 通过”对象.属性”或”对象.方法”的方式,可多次给属性赋值

关键字:final

Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。

  • final 标记的类不能被继承。 提高安全性,提高程序的可读性。 String类、SystemStringBuffer
  • final 标记的方法不能被子类重写。 比如:Object类中的getClass()
  • final 标记的变量 ( 成员变量或局部变量 ) 即称为常量。 名称大写,且只能被赋值一次
  • final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。 final double MY_PI = 3.14;
 //final修饰类 中国古代,什么人不能有后代,就可以被final声明,称为“太监类”!
final class A{
}
class B extends A{ //错误,不能被继承。
}
 
//final修饰方法
class A {
    public final void print() {
        System.out.println("A");
    }
}
class B extends A {
    public void print() { // 错误,不能被重写。
        System.out.println("B");
    }
}
 
// final修饰变量——常量 常量名要大写,内容不可修改。——如同古代皇帝的圣旨。
 
class A {
    private final String INFO = "1"; //声明常量
     public void print() {
    //The final field A.INFO cannot be assigned
    //INFO = "ONE";
    }
}
 
//static final:全局常量

关键字 final 应用举例

public final class Test {
    public static int totalNumber = 5;
    public final int ID;
    public Test() {
        ID = ++totalNumber; // 可在构造器中给final修饰的“变量”赋值
    }
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.ID);
        final int I = 10;
        final int J;
        J = 20;
        J = 30; // 非法
    }
}

总结

package com.jyc.p2;
/*
* 一.static关键字的使用
*   static可以用来修饰 属性 方法 构造器 内部类
*   使用static修饰属性 :静态变量
*       属性使用static修饰又分为静态属性(类变量) vs 非静态属性(实例变量)
*       实例变量:我们创建了类的多个对象,每个对象都有独立的一套类中的非静态属性.当修改一个对象中的
*                非静属性时,不会导致对象中同样的属性值的修改
*       静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量
*                其他对象调用此静态变量时,是修改过的
*      static修饰符的其他说明
*         1.静态变量随着类的加载而加载(可以通过类.静态变量的方式进行调用)
*         2.静态变量的加载要早于对象的创建
*         3.由于类只会加载一次,则静态变量在内存中也会存在一份,存在方法区的静态域中
*         4.类不可以调用实例变量
*
*      例如 System.out  Math.PI
*
*   使用static修饰方法
*    1. 随着类的加载而加载,可以通过类.静态方法的方式进行调用
*    2.不能调用非静态方法
*    3.静态方法中只能调用静态的方法或属性,非静态的方法中,既可以调用非静态的方法和属性,也可以调用静态
*    的方法和属性
*
*   static注意点
*   在静态的方法内不能使用this super 关键字
*   从生命周期的角度理解
*   属性是可以被多个对象共享的,不会随着对象的不同而不同
*   操作静态的方法,通常设置为static
*   工具类中的方法,习惯上声明为static 比如 Math.Arrays
*
* 二 单例设计模式
*  所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
*
* 饿汉式 vs 懒汉式
* 懒汉式 :延迟 对象的创建
* 饿汉式: 对象加载时间过长,线程安全
*
* 三 main方法的使用
*   1.mian()程序的入口
*     public static void main(String[] args) {}(也是普通的静态方法)
*     也可以作为我们与控制台交互的方式
*
* 四 代码块(或者初始化)
*   1.代码块的作用 用来初始化类 对象
*   2.代码块只能使用static修饰
*   3.分类 静态代码块 vs 非静态代码块
*   5.静态代码块
*       内部可以有输出语句
*       随着类者加载而执行,而且只执行一次
*       初始化类信息
*       如果一个类中定义了多个代码块,则按照声明的先后顺序执行
*       静态代码块的执行由于非静态代码块
*       静态代码块内可以调用静态属性静态方法,不能调用非静态结构
*
*     非静态代码块
*       内部可以有输出语句
*       随着对象的创建而执行,每创建一个对象执行一次
*       作用:可以对对象的属性进行初始化
*       如果一个类中定义了多个代码块,则按照声明的先后顺序执行
*       非静态代码块可以调用静态属性,静态方法 或非静态属性 非静态方法
*
*   属性可以赋值的位置
*   1.默认初始化
*   2.显示初始化
*   3.构造器中初始化
*   4.有了对象以后 可以对象.属性 对象.方法进行初始化
*   5.代码块中赋值
*   赋值顺序 1--> 2/5--> 3 -->4
*
* 五  final关键字 最终的
*  final 可以修饰的结构  类 方法 变量
*
*  final修饰一个类 此类不能够在继承
*   比如 String System
*
*  final修饰一个方法 表明方法不可以被重写
*    比如 object.getClass()
*
*  final可以用来修饰变量  此时的变量就是常量了
     final修饰属性 可以考虑赋值的位置有 显示初始化 代码块中 构造器中
     final修饰属性 修饰局部变量
        尤其是final修饰形参时,表明形参是一个常量,当我们调用此方法时给常量形参赋一个实参
        一但赋值以后,就只能在方法体内使用形参,但不能重新赋值
     static final:用来修饰属性 全局常量
     static final:用来修饰方法 不能被继承
 
 
* */
public class test1 {
    public static void main(String[] args) {
        Chinese c1=new Chinese("zs",18);
        Chinese c2=new Chinese("ls",22);
        c1.nation="中国";
 
        System.out.println(c2.nation);
        System.out.println("-----------单例设计模式饿汉式-------");
        Bank bank1=Bank.getInstance();
        Bank bank2=Bank.getInstance();
        System.out.println(bank1==bank2);
        System.out.println("-----------代码块-------");
 
        System.out.println(Chinese.desc);
        System.out.println(c2.language);
 
    }
}
class Chinese{
    String name;
    int age;
    String language;
    final  int width=10;
    final  int left;
    final  int right;
    static  String nation;
    static  String desc="我是一个人";
    Chinese(){
        right=100;
    }
    Chinese( String name, int age){
        this.age=age;
        this.name=name;
        right=100;
 
    }
 
    public  static  void show(){
        System.out.println("show执行");
        //name="tom";无法从静态上下文中引用非静态 变量
        nation="中国";//可以调用静态结构
    }
    //代码块
    {
        System.out.println("代码块");
        language="汉语";
        show();
        left=100;
    }
    static {
        System.out.println("静态代码块");
        desc="我是一个中国人";
        show();
    }
}
//饿汉式
 class Bank{
    //私有化类的构造器
     private  Bank(){}
     //内部创建类的对象(要求此对象必须为静态的)
     private  static  Bank instance=new Bank();
 
     //通过公共的静态对象 返回类的对象
     public  static  Bank getInstance(){
         return  instance;
     }
 }
 //饿汉式
 class Change{
    private  Change(){}
    //声明当前类对象,没有初始化
    private   static  Change instance =null;
    //声明public static的返回当前类对象方法
    public  static Change getInstance(){
        if (instance==null){
            instance= new Change();
      }
    return  instance;
}
 
 }
 
// class  C extends  String{} 不能继承
 
//class A{  不能重写
// final public  void  show(){}
//}
//class  B extends  A{
//    public  void  show(){}
//}