zoukankan      html  css  js  c++  java
  • String,static,final

    1. String####

    下面代码创建了几个对象?

    String s1 = new String("Hello");
    String s2 = new String("Hello");
    

    要想答对这道题,需要考虑String的一个常量池的概念。在执行代码的时候,首先会判断字符串常量池中是否存在"Hello",如果存在,直接返回该对象的引用,那对于本题就是创建2个对象。如果不存在,则现在常量池中创建,加起来就三个对象了(一个对象两个引用)。

    1. 什么样的字符串才会放在常量池中呢?
    首先,只有字符串对象才会放在常量池中。(String a = "abc","abc"是字符串对象)
    其次,只有在编译期确定的字符串才会被放在常量池中。
    
    2. 考虑一下,常量池实在堆区还是方法区?
    jdk1.6以及之前,是在方法区的,1.7及以后,是在堆区的。
    

    下面代码的结果呢?创建了几个对象呢?

    String s1 = "Java";
    String s2 = "从0到1";
    String s3 = s1 + s2;
    String s4 = "Java从0到1";
    System.out.println(s3 == s4);
    

    不考虑常量池中存在字符串,首先一眼看出至少三个,"Java","从0到1","Java从0到1"。比较麻烦的就是String s3 = s1 + s2;我们知道的是"Java","从0到1","Java从0到1"是在编译期确定,放在字符串常量池中的。s3的值在编译期间确定不了,是放在堆中,最后的值是"Java从0到1",这是第四个了,那这个值是如何得到的呢?s1和s2相加得到,但是s1和s2是在常量池中的,s3是在常量池之外的堆中的,所以必须拷贝s1和s2到常量池外的堆中,这又是两个,现在看来,一共是创建了6个对象

    那程序的结果就很明显了,结果是false。

    加深理解,可以运行下面的代码:

    String s1 = new String("Hello");
    String s2 = s1;
    s2 = "Java";
    System.out.println(s1);
    System.out.println(s2);
    System.out.println("==========================");
    String s3 = "Java";
    String s4 = new String("Java");
    System.out.println(s2 == s3);
    System.out.println(s3 == s4);
    System.out.println(s1.hashCode());
    System.out.println(s2.hashCode());
    System.out.println(s3.hashCode());
    System.out.println(s4.hashCode());
    System.out.println("===========================");
    System.out.println(System.identityHashCode(s1));
    System.out.println(System.identityHashCode(s2));
    System.out.println(System.identityHashCode(s3));
    System.out.println(System.identityHashCode(s4));
    

    要注意的是String s = "Hello" 和 String s = new String("Hello"); 的区别,在我看来,前一个s是个引用,放在常量池中(有点疑惑还得认真研究),而s是存放在堆上非常量池中,"Hello"是放在常量池中。

    2. String被设计为不可变的好处。####

    1. 被设计为不可变,字符串池才可以实现。
    2. 字符串不可变,不会造成线程安全问题,同一个字符串实例可以被多个线程共享,不用考虑同步,因为String本来就是线程安全。
    3. 字符串的不可变,保证了哈希码的唯一性,在创建的时候hashcode就被缓存,不用重新计算。使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
    

    3. final修饰类####

    首先要明确,final修饰的类是不可被继承的。比如说String类就是被final修饰符修饰,就不可被继承。相当于上锁,

    什么时候使用?当你确定某一个类是完美的,不需要进行任何的修改和扩展,这个时候可以讲类声明为final。

    4. final修饰成员变量####

    final修饰的成员变量只可被初始化一次,不可更改。

    5. final修饰方法####

    final修饰方法,该方法不可被子类重写覆盖,但可以在本类中进行重载,并且可以被正常调用。

    6. final修饰成员变量和普通变量的区别####

    String a = "hello1";
    final String b = "hello"; 
    String c = "hello"; 
    String d = b + 1;
    String e = c + 1;
    
    System.out.println(a == d);
    System.out.println(a == e);
    

    结果是什么呢?true和false!!!为什么呢?这个原因是因为final修饰变量在编译期间可以被直接替换,也就意味着在d=b+1的时候,d="hello"+1,即为"hello1",编译期确定的值在常量池中,d和a的引用是相同的,所以a==b是true,而e是在运行期确定的,在堆的非常量池中,引用不同,所以a==e是false。

    7. final和static####

    首先注意:

    static用来表示唯一,独此一份,也即是静态,但是是可变的。
    final用来表示不可变。
    

    理解:static表示无论创建多少个对象,每个对象中被static修饰的成员变量都是相同的值。但是fianl表示每个对象中被fianl修饰的成员变量是不可更改的,一旦确定就不可能再修改。

    8. static方法####

    static方法叫做静态方法,静态方法属于类,不属于对象,也就是可以直接使用类名来调用。在静态方法中不能调用非静态方法和非静态成员变量,但是非静态方法可以访问静态方法和成员变量(这两句话怎么理解呢?可以从加载类的顺序考虑,类的加载会先加载静态的成员变量和方法,之后是构造方法,非静态的成员变量和方法)。

    还有一个问题,就是构造方法是静态的吗?经过尝试,构造方法不能使用static修饰,但是在静态代码块中可以直接调用,因此可以得出,构造方法不是静态的,但他具有静态方法的一些特性,比如说可以在静态方法中被调用。

    9. static成员变量####

    静态变量被所有对象共享,即属于类,也就是在初次加载的时候被初始化,之后每次创建对象都不会初始化。而非静态变量是属于对象的,在每次创建对象都会有不同的赋值。

    10. static代码块####

    静态代码块可以用来优化程序性能,对于某些变量,只需要初始化一次等,这个时候就可以放在静态代码块,静态代码块永远只会执行一次。

    11. static注意点####

    • static是不允许用来修饰局部变量。
    • static变量和方法可以使用对象来调用(this等)。
    • java类关于静态的加载时间,单个类的话,没有创建对象,只会执行静态成员变量和静态代码块。创建对象会在前面的基础上依次执行初始化成员变量,代码块,构造方法。也就是这样: 静态成员变量 --> 静态代码块 --> 成员变量 --> 代码块 --> 构造函数。 如果存在子父类继承呢?父类静态成员变量 --> 父类静态代码块 -->子类静态成员变量 --> 子类静态代码块 --> 父类成员变量 --> 父类代码块 --> 父类构造函数 --> 子类成员变量 --> 子类代码块 --> 子类构造函数

    按照上面的顺序看看下面的代码的执行结果:

    public class Test {
       Person person = new Person("Test");
       static{
           System.out.println("test static");
       }
        
       public Test() {
           System.out.println("test constructor");
       }
        
       public static void main(String[] args) {
           new MyClass();
       }
    }
    
    class Person{
       static{
           System.out.println("person static");
       }
       public Person(String str) {
           System.out.println("person "+str);
       }
    }
    
    
    class MyClass extends Test {
       Person person = new Person("MyClass");
       static{
           System.out.println("myclass static");
       }
        
       public MyClass() {
           System.out.println("myclass constructor");
       }
    }
    
    // 结果为
    test static
    myclass static
    person static
    person Test
    test constructor
    person MyClass
    myclass constructor
  • 相关阅读:
    用户登录设计 单点登录 记住密码等
    相同类名称但不同包启动报错
    IDEA项目搭建十一——添加拦截器、忽略URL大小写、启动事件
    IDEA项目搭建七——使用Feign简化消费者端操作
    java enum使用方法
    IDEA项目搭建十——使用slf4j和logback进行日志记录
    IDEA项目搭建九——MybatisPlus多数据库实现
    IDEA项目搭建八——使用MybatisPlus简化数据库交互
    IDEA项目搭建六——使用Eureka和Ribbon进行项目服务化
    新建Git仓库并关联远程Gitlab
  • 原文地址:https://www.cnblogs.com/wadmwz/p/9694660.html
Copyright © 2011-2022 走看看