ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java基础

2022-01-14 11:03:02  阅读:164  来源: 互联网

标签:Java num1 num2 int 基础 128 Integer public


什么是序列化

java中的序列化(serialization)机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输、或者持久化存储到数据库或文件系统中;然后在需要的时候,可以根据字节流中的信息来重构一个相同的对象。序列化机制在java中有着广泛的应用,EJB、 RMI等技术都是以此为基础的。

Java的序列化机制只序列化对象的属性值,而不会去序列化什么所谓的方法。其实这个问题简单思考一下就可以搞清楚,方法是不带状态的,就是一些指令,指令是不需要序列化的,只要你的JVM classloader可以load到这个类,那么类方法指令自然就可以获得。

序列化真正需要保存的只是对象属性的值,和对象的类型。

HashCode理解

1、Object类的hashCode:返回对象的内存地址经过处理后的结构,由于每个对象的内存地址 (JVM虚拟出来的内存地址) 都不一样,所以哈希码也不一样.

  两个对象要完全相同必须哈希值一样。比较用equal()

2、String类的hashCode:根据String类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串内容相同,返回的哈希码也相同。

  比较字符串内容相等。用equal()

3、Integer类的hashCode:返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer i1=new Integer(100),i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样

  比较包装数值类型相等。用equal()

4、简单类型比较相等。用==

String、StringBuffer与StringBuilder之间区别

在这里插入图片描述

String 类中常用的方法

在这里插入图片描述

为什么要有包装类

在这里插入图片描述
为什么存在这两种类型呢?

一:我们都知道在Java语言中,new一个对象存储在堆里,我们通过栈中的引用来使用这些对象;但是对于经常用到的一系列类型如int,如果我们用new将其存储在堆里就不是很有效——特别是简单的小的变量。所以就出现了基本类型,同C++一样,Java采用了相似的做法,对于这些类型不是用new关键字来创建,而是直接将变量的值存储在栈中,因此更加高效。

二:Java是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到Object[]数组或集合中的操作等等。
注意:比如需要往ArrayList,HashMap中放东西时,像int,double这种基本类型是放不进去的,因为容器都是装object的,这是就需要这些基本类型的包装器类了

  1. 为了解决这个不足,Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
  2. 包装类均位于java.lang包,八种包装类和基本数据类型的对应关系如表

二者的区别:

  1. 声明方式不同
    基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;
  2. 存储方式及位置不同
    基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
  3. 初始值不同
    基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;
  4. 使用方式不同
    基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到

自动装箱拆箱是什么

简单一点说:
装箱就是自动将基本数据类型转换为包装器类型;
拆箱就是自动将包装器类型转换为基本数据类型。

在这里插入图片描述

public class Main {
    public static void main(String[] args) {
    //自动装箱
    Integer total = 99;

    //自定拆箱
    int totalprim = total;
    }
}

详解

Integer total = 99; 
执行上面那句代码的时候,系统为我们执行了: 
Integer total = Integer.valueOf(99);

int totalprim = total; 
执行上面那句代码的时候,系统为我们执行了: 
int totalprim = total.intValue();

装箱

1.首先来看看Integer.valueOf函数:

public static Integer valueOf(int i) {
	return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
 }

它会首先判断i的大小:如果i小于-128或者大于等于128,就创建一个Integer对象,否则执行SMALL_VALUES[i + 128]。

1.1 首先我们来看看Integer的构造函数:

private final int value;

public Integer(int value) {
    this.value = value;
}

public Integer(String string) throws NumberFormatException {
    this(parseInt(string));
}

它里面定义了一个value变量,创建一个Integer对象,就会给这个变量初始化。
第二个传入的是一个String变量,它会先把它转换成一个int值,然后进行初始化。

1.2 下面看看**SMALL_VALUES[i + 128]**是什么东西

 private static final Integer[] SMALL_VALUES = new Integer[256]; 

它是一个静态的Integer数组对象。
也就是说最终valueOf返回的都是一个Integer对象。

装箱总结:装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能。

拆箱

@Override
public int intValue() {
     return value;
}//直接返回value值即可。

1、i >= 128 || i < -128 =====> new Integer(i)
2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]

private static final Integer[] SMALL_VALUES = new Integer[256];
SMALL_VALUES本来已经被创建好,

也就是说,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象

然而在i >= 128 || i < -128是会创建不同的对象,

举例

public class Main {
    public static void main(String[] args) {

        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println(i1==i2);  //true
        System.out.println(i3==i4);  //false
    }
}

1、i1和i2会进行自动装箱,执行了valueOf函数,它们的值在(-128,128]这个范围内,它们会拿到SMALL_VALUES数组里面的同一个对象SMALL_VALUES[228],它们引用到了同一个Integer对象,所以它们肯定是相等的。

2、i3和i4也会进行自动装箱,执行了valueOf函数,它们的值大于128,所以会执行new Integer(200),也就是它们会分别创建两个不同的对象,所以它们肯定不等。

public class Main {
    public static void main(String[] args) {

        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;

        System.out.println(i1==i2); //false
        System.out.println(i3==i4); //false
    }
}

因为对于Integer,在(-128,128]之间只有固定的256个值,所以为了避免多次创建对象,我们事先就创建好一个大小为256的Integer数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。

但是对于Double类型来说,我们就不能这样做,因为它在这个范围内个数是无限。

总结:在某个范围内的整型数值的个数是有限的,而浮点数却不是。所以在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。

另外一种情况

public class Main {
    public static void main(String[] args) {

        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;

        System.out.println(i1==i2);//true
        System.out.println(i3==i4);//true
    }
}

可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象。

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

可以看到它并没有创建对象,因为在内部已经提前创建好两个对象,因为它只有两种情况,这样也是为了避免重复创建太多的对象。

public static final Boolean TRUE = new Boolean(true);
 
public static final Boolean FALSE = new Boolean(false);

其他情况

1 Integer num1 = 400;  
2 int num2 = 400;  
3 System.out.println(num1 == num2); //true
说明num1 == num2进行了拆箱操作
1 Integer num1 = 100;  
2 int num2 = 100;  
3 System.out.println(num1.equals(num2));  //true

equals源码:

1 @Override
2 public boolean equals(Object o) {
3     return (o instanceof Integer) && (((Integer) o).value == value);
4 }

它必须满足两个条件才为true:
1、类型相同
2、内容相同

我们传入的是一个int类型,所以首先会进行装箱,类型相同了,然后比较,返回true。

1 Integer num1 = 100;  
2 int num2 = 100;  
3 Long num3 = 200;  
4 System.out.println(num1 + num2);  //200
5 System.out.println(num3 == (num1 + num2));  //true
6 System.out.println(num3.equals(num1 + num2));  //false

1、当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。

2、对于num3.equals(num1 + num2)为false的原因很简单,我们还是根据代码实现来说明:

1 @Override
2 public boolean equals(Object o) {
3     return (o instanceof Long) && (((Long) o).value == value);
4 }

它必须满足两个条件才为true:
1、类型相同
2、内容相同
上面返回false的原因就是类型不同。

1 Integer num1 = 100;
2 Integer num2 = 200;
3 Long num3 = 300;
4 System.out.println(num3 == (num1 + num2)); //true

首先对num3进行拆箱(执行num3的longValue得到基础类型为long的值300)
然后对num1和mum2进行拆箱(分别执行了num1和num2的intValue得到基础类型为int的值100和200)
然后进行相关的基础运算。

 int num1 = 100;
 int num2 = 200;
 long mum3 = 300;
 System.out.println(num3 == (num1 + num2)); //true
 int num1 = 100;
 int num2 = 200;
 long mum3 = 300;
 System.out.println(num3 == num1); //false

所以,当 “==”运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)

总结:

1、需要知道什么时候会引发装箱和拆箱
2、装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。

3、equals(Object o) 因为原equals方法中的参数类型是封装类型,所传入的参数类型(a)是原始数据类型,所以会自动对其装箱,反之,会对其进行拆箱

4、当两种不同类型用==比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱。

标签:Java,num1,num2,int,基础,128,Integer,public
来源: https://blog.csdn.net/m0_46407213/article/details/122474501

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有