java基础常见问题

by shihang.mai

1. String的intern()

1.1 字符串的拼接

先来看看字符串的拼接

public static void main(String[] args) {
        String s1 = "a"+"b"+"c";
        String s2 = "abc";
        String s3 = s2+"";
        final String s4 = "abc";
        String s5 = s4+"";
        //true
        System.out.println("s1==s2:" + (s1 == s2));
        //false
        System.out.println("s2==s3:"+ (s2 == s3));
        //true
        System.out.println("s4==s5:"+ (s4 == s5));

    }

我们用javac编译文件,然后javap -c class得到下面内容

Compiled from "Test.java"
public class com.qdama.intl.common.service.listen.Test {
  public com.qdama.intl.common.service.listen.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String abc
       2: astore_1
       3: ldc           #2                  // String abc
       5: astore_2
       6: new           #3                  // class java/lang/StringBuilder
       9: dup
      10: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      13: aload_2
      14: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      17: ldc           #6                  // String
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      25: astore_3
      26: ldc           #2                  // String abc
      28: astore        5
      30: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      33: new           #3                  // class java/lang/StringBuilder
      36: dup
      37: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      40: ldc           #9                  // String s1==s2:
      42: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      45: aload_1
      46: aload_2
      47: if_acmpne     54
      50: iconst_1
      51: goto          55
      54: iconst_0
      55: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
      58: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      61: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      64: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      67: new           #3                  // class java/lang/StringBuilder
      70: dup
      71: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      74: ldc           #12                 // String s2==s3:
      76: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      79: aload_2
      80: aload_3
      81: if_acmpne     88
      84: iconst_1
      85: goto          89
      88: iconst_0
      89: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
      92: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      95: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      98: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
     101: new           #3                  // class java/lang/StringBuilder
     104: dup
     105: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
     108: ldc           #13                 // String s4==s5:
     110: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     113: ldc           #2                  // String abc
     115: aload         5
     117: if_acmpne     124
     120: iconst_1
     121: goto          125
     124: iconst_0
     125: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
     128: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     131: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     134: return
}

可以看到

  1. 在编译期,s1直接等于abc,所以s1==s2 -> true
  2. s3实际上是通过StringBulider构造而来,所以s2==s3 -> false
  3. 对于s4加上了final,表明是常量,s5在编译期直接赋值abc,所以s4==f5 -> true

1.2 String常量池

底层实际就是一个hash表,数组加链表.可通过-XX:StringTableSize=数值改变数组的大小

1.3 intern()

对于intern(),需要分jdk 1.6和1.7以上描述

  • jdk1.6
    当字符串.intern()
    如果常量池不存在该字符串常量,那么就会把复制一份到常量池,并返回常量池中的对象
    如果常量池存在该字符串常量,直接返回常量池中的对象
  • jdk 1.7以上
    当字符串.intern()
    如果常量池不存在该字符串常量,那么就会把地址复制一份到常量池,并返回常量池中的对象地址
    如果常量池存在该字符串常量,直接返回常量池中的对象

举例说明A

public static void main(String[] args) {

       //常量池会有一个a,堆中有s1(a对象)
        String s1 = new String("a");
       //执行该语句,因为常量池已经有a,故返回的是常量池的对象,但这里没变量接收
        s1.intern();
        //这里s2直接取常量池中的a
        String s2 = "a";
        //s1是堆中的a对象,s2是常量池的a对象,故结果为false
        System.out.println(s1 == s2);

    }

举例说明B

public static void main(String[] args) {
       //常量池b、c,堆s3(bc对象)
        String s3 = new String("b") + new String("c");
      //执行该语句,这里分不同的版本
      //当为1.6时,直接将bc值复制一份到常量池,形成常量池中有b、c、bc,堆中还是s3(bc对象)
      //当为1.7时,将s3的地址放到常量池,形成常量池中有b、c、s3地址
        s3.intern();
      //当为1.6时,那么s4直接取得常量池的bc
      //当为1.7时,bc在常量池实际是s3的地址
        String s4 = "bc";
      //当为1.6时,s3是堆中的s3(bc对象),s4时常量池的bc,故结果为false
      //当为1.7时,s4即为s3,故结果为true
        System.out.println(s3 == s4);

    }

举例说明C

public static void main(String[] args) {
        //常量池d、e,堆s5(de对象)
        String s5 = new String("d") + new String("e");
        //常量池放入de
        String s6 = "de";
        /*执行此方法,将de放入常量池,但是上一步常量池已经有de,
        故这里返回常量池的de,但是没变量接收,等于没做任何事*/
        s5.intern();
        //s5是堆对象,s6是常量池对象,故结构为false
        System.out.println(s5 == s6);

    }

2. ==和equals

看看Object类的equals源码

public boolean equals(Object obj) {
        return (this == obj);
    }

其实它于==一样,都直接是比较两个对象地址是否一样
对于String的equals,实际上是重写了Object的equals方法的

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[I])
                        return false;
                    I++;
                }
                return true;
            }
        }
        return false;
    }

3. equals与hashcode

equals相同,那么它们的hashcode必须相等
我们重写equals都必须重写hashcode,我们用反证法说明。
当我们如果只重写equals时,那么在使用集合时,会出现逻辑性错误。

  • HashSet
    它在加入元素时,先会判断hashcode,如果hashcode相等再判断equals。当只重写equals的话,那么Set中就可能出现相同的元素了

4. 序列化和反序列化

序列化: 对象->字节流
反序列化: 字节流->对象

java实现序列化两种方式:实现Serializable接口或者实现Exteranlizable接口

对于Serializable
static修饰和被transient修饰的属性不会参加序列化,除了自身的static serialVersionUID。
下面是找到的源码过滤的地方

private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
        Field[] clFields = cl.getDeclaredFields();
        ArrayList<ObjectStreamField> list = new ArrayList<>();
        //重点
        int mask = Modifier.STATIC | Modifier.TRANSIENT;

        for (int i = 0; i < clFields.length; i++) {
            //重点
            if ((clFields[i].getModifiers() & mask) == 0) {
                list.add(new ObjectStreamField(clFields[i], false, true));
            }
        }
        int size = list.size();
        return (size == 0) ? NO_FIELDS :
            list.toArray(new ObjectStreamField[size]);
    }

4.1 需要序列化的原因

首先明确,其实不用序列化,一样可以存储数据的。任何数据在计算机中的存储都是0,1进制的,所以即使不序列化对象,依然可以传输。即结构对象可以进行跨网络传输和持久化存储。

  1. 筛选数据,防止重复存储
    序列化所做的工作除了将数据以二进制存入本地外,还要提供筛选数据,防止重复存储等功能。但是如果直接赋值内存中的数据,肯定达不到筛选数据,防止重复存储等功能。
  2. 跨平台、跨语言时
    将java 对象序列化成 xml 或者 json 形式。这样即使是 python 等非java语言都可以直接使用这个xml 或者json 对象得到自己需要的信息了

序列化使得对象信息更加普通化,可读化。这样就可以使得别的进程,别的语言,别的平台都能够知道这个对象信息,从而保证了对象信息的持久化

博主:https://blog.csdn.net/liu16659/article/details/85793686

4.2 serialVersionUID作用

  • 当一个对象实现Serializable接口,但是没指定serialVersionUID,那么java在序列化时,根据属性生成一个serialVersionUID。当修改对象属性后,再将原本序列化的对象反序列化,会报错
  • 当一个对象实现Serializable接口,指定serialVersionUID,当修改对象属性后,再将原本序列化的对象反序列化,不会报错。

在开发代码时,不指定这个,旧数据就无法反序列化,会出很大问题

4.3 序列化和单例

直接看-设计模式之单例,写得很清楚

4.4 反序列化安全

JWT,待完善

5. 异常

异常分类

6. 克隆

快速获取一个对象的副本。实现Cloneable标记接口,重写Object类的clone()方法

6.1 浅克隆

创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址

6.2 深克隆

创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址

6.2.1 改写引用类型的clone

引用类型也实现Cloneable,并且持有该类型的类的clone()将该引用类型重新set进去即可

public Object clone() {
        Cat clone = null;
        try {
            clone = (Cat) super.clone();
            clone.setSkill(this.getSkill().clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

6.2.2 序列化克隆

实现Cloneable, Serializable

public Object clone() {

        ByteArrayOutputStream bos = null ;
        ObjectOutputStream oos = null ;
        ByteArrayInputStream bis = null ;
        ObjectInputStream ois  = null ;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);


            //反序列化
            bis = new ByteArrayInputStream( bos.toByteArray() );
            ois = new ObjectInputStream( bis );
            Cat copy = (Cat) ois.readObject();


            return copy;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }finally{
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

正因为对象可序列化克隆,所以写单例时必须考虑防止序列化影响。所以在单例中加入方法readResolve()即可,这是因为在反序列化的源码中,如果目标类有readResolve方法,那就通过反射的方式调用要被反序列化的类中的readResolve方法,返回一个对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容