java - static - final

来源:互联网 发布:怎么查询端口号 编辑:程序博客网 时间:2024/05/17 07:54

static关键字


如果一个类成员被声明为static,它就能够在类的任何对象创建之前被访问,而不必引用任何对象。static表示静态的意思,用来修饰类,变量,方法。

一、static修饰类
        static只能修饰内部类,相当于外部类的成员,又叫静态成员内部类。例如:
public class OuterClass {static class InnerClass{}}
        何时使用静态成员内部类?首先,用内部类是因为内部类与所在外部类有一定的关系,往往只有该外部类调用此内部类。所以没有必要专门用一个Java文件存放这个类。静态都是用来修饰类的内部成员的。它唯一的作用就是随着类的加载(而不是随着对象的产生)而产生,以致可以用类名+静态成员名直接获得。
class OuterClass {// 成员静态内部类class InnerClass1 {}// 成员静态内部类static class InnerClass2 {}}public class Test {public static void main(String[] args) {//调用非静态内部类OuterClass oc = new OuterClass();OuterClass.InnerClass1 InnerClass1 = oc.new InnerClass1();//调用静态内部类OuterClass.InnerClass2 oci2 = new OuterClass.InnerClass2();}}
二、static修饰的变量
        static的变量实质上就是全局变量,也成类变量,是在类加载的时候初始化,且只被初始化一次。因此当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共同拥有的一个属性。在程序中任何对象对静态变量做修改,其他对象看到的是修改后的值。因此类变量可以用作计数器。另外,类变量可以用类名直接访问,而不必需要对象。 

        static变量前可以有private修饰,表示这个变量可以在类的静态代码块中或者类的其他静态成员方法中使用。但是不能在其他类中通过类名来直接引用。       

        静态变量和实例变量区别?对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。 
       
        何时使用类变量? 在对象之间共享值时。方便访问变量时。

三、static修饰的方法
        static修饰的成员方法又叫静态成员方法,也叫嵌套类,独立与该类的任何对象,不依赖类特定的实例,被类的所有实例共享。静态成员方法只能继承不能重写,子类写了和父类的同样方法,实质是把父类的静态方法隐藏了。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。

        何时使用静态成员方法?一切不需要实例化就可以有确定行为方式的函数都应该设计成静态的。比如:某些方法不想依赖具体实例,那就可以定义为静态成员方法,直接类名.调用。如果某个方法使用频率较高,本身通用性较强,无需初始化类成员变量,则可以使用静态方法,方便。工具类,多个类中需要调用并且是与对象无关的方法可设为静态方法,方便调用。可以实现某些特殊的设计模式,如Singleton。可以封装某些算法,比如数学函数,如sin,tan等,这些函数本就没必要属于任何一个对象,所以从类上调比较好

四、static代码块
        static代码块也叫静态代码块,是在类中独立于类成员的static语句块,不能出现在方法体中(静态成员方法也不行)。static代码块和类加载一样,先执行父类再执行子类。JVM加载类时会执行静态的代码块,如果静态代码块有多个,JVM将按照它在类中出现顺序依次执行,且每个只会被执行一次。
        
        如果想在项目启动时完成一些初始化工作,就需要它。因为类在加载时它就会主动执行的。

        静态代码块和静态成员方法区别? 两者都是项目启动时初始化的,区别在于一个是自动执行,一个是被调用时执行。

        总结:静态成员包括静态成员内部类,静态成员变量,静态成员方法都属于类的,不属于任何对象。只要这个类被加载,JVM就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。静态成员不能使用this或者super来调用

final关键字


final表示最终的意思,用来修饰类,变量(成员变量和局部变量),方法。

一、final修饰类
        当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是final类中的所有成员方法都会被隐式地指定为final方法。

        在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

二、final修饰方法
        使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。类的private方法会隐式地被指定为final方法。

三、final修饰变量
        对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。但是它指向的对象的内容是可变的。
        上面number和obj重新赋值都报错

        类的final变量和final局部变量的区别?final修饰的变量系统不会默认值,而要求构造器完成之前必须初始化(可选时机一声明时初始化,可选时机二非静态语句块中初始化,可选时机三构造器中进行初始化)。终态局部变量初始化值的时间只要在使用值之前就行。

        类的final变量和普通变量有什么区别?当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)。必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。
public class Test {    public static void main(String[] args)  {        String a = "helloworld";         final String b = "hello";        String c = b + "world";         System.out.println((a == c));                String d = "hello";        String e = d + "world";        System.out.println((a == e));    }}//true//false
        第一个比较结果为true,而第二个比较结果为fasle。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的  值。而对于变量d的访问却需要在运行时通过链接来进行。不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,比如下面的这段代码就不会进行优化:
public class Test {    public static void main(String[] args)  {        String a = "helloworld";         final String b = getHello();        String c = b + "world";         System.out.println((a == c));    }    public static String getHello() {        return "hello";    }}//false

final和static


static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。
public class Test {public static void main(String[] args) {MyClass myClass1 = new MyClass();MyClass myClass2 = new MyClass();System.out.println(myClass1.i);System.out.println(myClass1.j);System.out.println(myClass2.i);System.out.println(myClass2.j);}}class MyClass {public final double i = Math.random();public static double j = Math.random();}//0.8736942592066105//0.664092891332819//0.15996822755581497//0.664092891332819
        运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。从这里就可以知道final和static变量的区别了。

匿名内部类访问外部类中的局部变量必须是final属性


        因为局部变量的生命周期与局部内部类的对象的生命周期的不一致,只有修饰为final生命周期才能一致。

        因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。 

        感觉这样解释更容易理解,从栈和堆角度来说明,在Java中,方法的局部变量位于栈上,对象位于堆上。因为局部变量的范围被限制在该方法内,当一个方法结束时,栈结构被删除,该变量消失。但是,定义在这个类中的内部类对象仍然存活在堆上,所以内部类对象不能使用局部变量。除非这些局部变量被标识为最终的。    

关于final参数的问题


使用final修饰方法参数的目的是防止修改这个参数的值,同时也是一种声明和约定,强调这个参数是不可变的

先看个例子
public class Test {public static void main(String[] args) {MyClass mc = new MyClass();int number = 0;mc.changeVaule1(number);System.out.println(number);String str = "hello";mc.changeVaule2(str);System.out.println(str);}}class MyClass {void changeVaule1(int number) {number++;}void changeVaule2(String str) {str = str + "world";}}//0//hello
        以上结果是不是出乎意料。殊不知,方法changeValue和main方法中的变量i根本就不是一个变量,因为java参数传递采用的是值传递,对于基本类型的变量,相当于直接将变量进行了拷贝。

再看下面的例子
public class Test {public static void main(String[] args) {MyClass myClass = new MyClass();StringBuffer buffer = new StringBuffer("hello");myClass.changeValue(buffer);System.out.println(buffer.toString());}}class MyClass {void changeValue(StringBuffer buffer) {buffer.append("world");}}//helloworld
        以上结果是不是我们预期的结果。对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。
原创粉丝点击