java热点问题

前言

本来平平无奇的知识点,在工作中很多人会选择忽略,但是一旦有积累之后,你才会发现这些看似理所应当的点,其实对整个代码影响是深远的。例如初级程序员看来对象深浅拷贝,关系着引用传递,一处改动处处改动,但作为久经沙场的可能意识到二叉树的生成拷贝时会出现很多问题。

求值策略

分为严格和非严格两大基本策略类型。一个语言可以组合多个求值策略,例如C++组合了值传递引用传递
值传递:实参的值复制一个副本,把这个副本传递给被调用函数的形参,被调用函数改变形参,不会改变实参的值。
引用传递:实参的引用(引用其他对象的对象,比如指针)直接传递个形参,函数调用过程中形参被改变,实参就被改变了。
共享对象传递:实参的引用拷贝一个副本,把这个地址副本传递给形参,函数调调用过程中形参内容被改变,实参就被改变了。
附件—恢复传递:向被叫进程传递一个实参的引用副本,实参与调用进程没有关系,进程调用结束,再把副本中更新的内容复制回实参引用。


在这里插入图片描述

共享对象传递的过程和值传递一样,实参拷贝成副本,传递副本。但结果又与引用传递一样:调用者改变形参内容,实参也就改变了。对于该问题关注过程而非结果,
我们知道目前java进程间通信在jvm层面还没有实现,但附件—恢复传递为它提供了一个策略。python的进程间通信有一个Server Process模型和他类似。只是传递的不是实参的副本,而是是一个代理对象。

数据类型

java是面向对象语言,但它数据类型保留了8种基本数据类型,基本数据类型值传递,引用类型共享对象传递。他们过程又都一样,复制、传递。所以通常我们说java值传递。但肯定没有引用传递
数据类型 { 基本类型 引用类型 数据类型\begin{cases} 基本类型\\引用类型& \end{cases} 数据类型{基本类型引用类型

基本数据类型

八种基本类型对应的装箱类型。基本类型是内置类型,性能优于对象。但面向对象必须是以对象为主体,否则就无法实现面向对象的编程功能。所以就设计了基本类型的对应装箱类。比如实现泛型,必须是对象类型,泛型在编译阶段就会泛型擦除,这时类型就是Object,基本类型不属于类,所以会在编译期就报转型失败错误。

基本类型booleanbytecharshortIntlongfloatdouble
二进制位数18161632643264
装箱类型BooleanByteCharacterShortIntegerLongFloatDouble

引用类型

引用类型赋值操作用计算机科学求值策略看,它是共享对象传递,引用副本传递之后,两(多)个不同的引用指向同一个内存地址,因此对于java初学者会引发连锁反应,一处更改,处处变动。

java求值策略分析

基本数据类型

在这里插入图片描述

public static void main(String[] args) {
        int i = 1;
        System.out.println("传递之前变量i的值:"+i);
        product(i);
        System.out.println("传递之后变量i的值:"+i);
    }
    public static void product(int arg) {
        System.out.println("操作之前形参arg的值:"+arg);
        arg+=1;
        System.out.println("操作之后形参arg的值:"+arg);
    }
    /**
    传递之前变量i的值:1
	操作之前形参arg的值:1
	操作之后形参arg的值:2
	传递之后变量i的值:1
    */

引用类型

在这里插入图片描述
对象中的String赋值,只是改变了属性引用指向的,外部对象引用指向没变,所以外部看来拿到那个一个对象,看到name属性的值变化了。并不违背String不可变性

public static void main(String[] args) {
        User us = new User();
        us.name="wellim";
        product(us);
        System.out.println("操作之后形参us.name的值:"+arg);
    }
    public static void product(User arg_user) {
        arg_user.name = "jack";
    }
    /*
    操作之后形参us.name的值: jack
    */

String

String也是引用类型,为什么特立独行,不会被改变。它是final,实际存储值的字节数组也是final,String对象一经创建,值就是不可变的,我们赋值,只是改变了这个引用的指向,指向了另一个常量池常量。看着似乎值改变了,其实并不是同一个内存区域。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
 private final byte[] value;
 }

在这里插入图片描述

public static void main(String[] args) {
        String s1 = "123";
        product(s1);
    }
    public static void product(String arg) {
    //只是s1引用副本值为123456。s1仍然是123
        arg+="456";
    }

StringBuilder

如果是引用传递,直接传递的是自己的引用,那么arg=null之后,最后一行输出应该是null,所以并不是引用传递,而是共享对象传递,传递的是引用副本.这时的StringBuilder和上面的User一样的,它继承自父类AbstractStringBuilderchar[] value,就相当于User的name属性,最终是value的引用指向了123456这个字符串。也就是说s1指向StringBuilder这个对象没有变,只是对象内属性指向变了,所以外界看来,s1这个对象被修改该。

 public static void main(String[] args) {
        StringBuilder s1 = new StringBuilder("123");
        System.out.println("调用之前:"+s1);
        product(s1);
        System.out.println("调用之后:"+s1);
    }
    public static void product(StringBuilder arg) {
        arg.append("456");
        System.out.println("调用之后arg值:"+arg);
        arg = null;
    }
    /*
    * 调用之前:123
	调用之后arg值:123456
	调用之后:123456
    */

装箱类

为了解决让基本类型也可以用泛型,就把它包装成对象。
装箱过程

/**
* 以iot这个类变量的装箱为例
*/
public class TestInt {
    private Integer iot = 1027;
}

iot变量的token
在这里插入图片描述
例如private Integer iot = 1027编译器会为我们自动装箱,调用Integer.valueOf(int i)方法把int类型的1027封装为Integer

/** VariableInitializer = ArrayInitializer | Expression
* 编译时语法分析要校验初始化器数组或者表达式类型
* Integer走的就是parseExpression
 */
    public JCExpression variableInitializer() {
        return token.kind == LBRACE ? arrayInitializer(token.pos, null) : parseExpression();
    }
  /**
  * 对Token流进行分析
  */
    JCExpression term3() {
    switch (token.kind) {
    case INTLITERAL: case LONGLITERAL: case FLOATLITERAL: case DOUBLELITERAL:
        case CHARLITERAL: case STRINGLITERAL:
        case TRUE: case FALSE: case NULL:
            if (typeArgs == null && (mode & EXPR) != 0) {
                selectExprMode();
                t = literal(names.empty);
            } else return illegal();
            break;
  }
}
	JCExpression literal(Name prefix) {
        return literal(prefix, token.pos);
    	}
    	/**
    	* 字面量分析
    	*/
	JCExpression literal(Name prefix, int pos) {
 		switch (token.kind) {
        case INTLITERAL:
            try {
                t = F.at(pos).Literal(
                    TypeTag.INT,
                    Convert.string2int(strval(prefix), token.radix()));
 	 }
	}
/** Convert string to integer.
     */
    public static int string2int(String s, int radix)
        throws NumberFormatException {
        if (radix == 10) {//如果是十进制,调用Integer的parseInt
            return Integer.parseInt(s, radix);
        } else {
            char[] cs = s.toCharArray();
            int limit = Integer.MAX_VALUE / (radix/2);
            int n = 0;
            for (char c : cs) {
                int d = Character.digit(c, radix);
                if (n < 0 ||
                    n > limit ||
                    n * radix > Integer.MAX_VALUE - d)
                    throw new NumberFormatException();
                n = n * radix + d;
            }
            return n;
        }
    }	
 public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }

        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+') {
                    throw NumberFormatException.forInputString(s, radix);
                }

                if (len == 1) { // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s, radix);
                }
                i++;
            }
            int multmin = limit / radix;
            int result = 0;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                int digit = Character.digit(s.charAt(i++), radix);
                if (digit < 0 || result < multmin) {
                    throw NumberFormatException.forInputString(s, radix);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s, radix);
                }
                result -= digit;
            }
            return negative ? result : -result;
        } else {
            throw NumberFormatException.forInputString(s, radix);
        }
    }
Symbol valueOfSym = lookupMethod(tree.pos(),
                                         names.valueOf,
                                         box,
                                         List.<Type>nil()
                                         .prepend(tree.type));

对象拷贝

一个对象要调用clone方法拷贝对象,就必须实现Cloneable,它是一个标记接口。就可以实现对象浅拷贝。如果要深拷贝就需要重写clone方法

浅拷贝

object的clone方法就是浅拷贝,当属性是基本类型或者String,直接clone()就可以实现浅拷贝。这里的String很特殊,它本身是个引用类型,按理需要深拷贝的,但是String又是final类,只要值一改变,就意味着jvm内部会新创建一个String对象,所以表面看着是String被深拷贝了。

深拷贝

当要拷贝的对象中属性有引用类型,就需要深拷贝。重写clone

Person p = super.clone();
//引用类型也要调用clone
this.car.clone();

但如果这种引用类型嵌套,我们岂不是需要每一个clone方法中都要重写调用?很麻烦。

序列化

每个类都实现Serializable接口,这也是一个标记接口。通过序列化,反序列化达到深拷贝的目的。反序列化的时候调用readOrdinaryObject,通过类描述器构建新的实例。这样就达到了两个完全不同的对象的目的。

obj = desc.isInstantiable() ? desc.newInstance() : null;

实例分析java对象中浅克隆和深克隆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值