Java中的默认类型:整数类型是 int 、浮点类型是 double 。

虽然float占4个字节,但是采用科学技术法,取值范围远远大于8个字节的long。

在这里插入图片描述

数据类型转换

Java程序中要求参与的计算的数据,必须要保证数据类型的一致性,如果数据类型不一致将发生类型的转换。

自动转换

当表达式两边数据类型不一致时,取值范围小的数据或者变量,可以直接赋值给取值范围大的变量

  1. 不需要进行代码的特殊处理
  2. byte,short,char数据,只要参加运算,就会自动转换成int类型
  3. 表达式最终的数据类型,一定是表达式中取值范围最大的类型
    byte、short、char -> int -> long -> float -> double
// 左边是long类型,右边默认是int类型,两边数据类型不一致
// 取值范围小的数据或者变量,可以直接赋值给取值范围大的变量

// int --> long,自动类型转换
// int4个字节,long8个字节,
// 自动类型转换只需要在前面补充4个字节的0,对值并没有影响
long num = 100;

double num = 100;

强制转换

浮点转换成整数,直接取消小数点,可能造成数据损失精度。

// 左边是byte类型,占一个字节,右边是int类型,占4个字节
// 从小到大的类型可以在前面添加缺少字节的数量的0
// 从大到小,需要强制类型转换,字节就会从左边开始被削减
int num = (int)6000000000L;

在这里插入图片描述

常量和变量的运算的注意事项

// 左边byte取值范围小,右边int取值范围大,理论说应该报错,但实际并没有
// 这么赋值的话,1代表着常量的角色
// 整数常量只要不超出对应的赋值变量的范围,可以直接赋值,相当于隐藏了一个强制类型转换



byte a = 1;

byte b = 2;

// 这个也不会报错,此时的 1 2 也属于常量,并且运算后的值也没有超出byte的范围
byte c = 1 + 2;

// a b 均为变量,值可能会超出byte的范围,所以这个不会自动强制类型转换,是报错的
byte d = a + b;

什么是引用

变量的实质是一小块内存单元。这一小块内存里存储着变量的值。

int a = 1; a就是变量的名,1就是变量的值。

A a =new A(); 而当变量指向一个对象时,这个变量就被称为引用变量

a就是引用变量,它指向了一个A对象,也可以说它引用了一个A对象。我们通过操纵这个a来操作A对象。 此时,变量a的值为它所引用对象的地址

  • 对于基本类型 num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。

  • 对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)

java是值传递还是引用传递

java是值传递,没有引用传递

面试官:你好,你能说出下面个程序的执行结果吗?

public class Test {
 
	public static void main(String[] args) {
		String name = "Scott";
		int age = 5;
		User user = new User();
		user.setName(name);
		user.setAge(age);
		System.out.println("before change user = " + user);
		
		change(user, name, age);
		System.out.println("name = " + name);
		System.out.println("age = " + age);
		System.out.println("after change user = " + user);
	}
	
	public static void change(User user, String name, int age) {
		name = "Tom";
		age = 20;
		user.setName(name);
		user.setAge(age);
	}
	
	static class User {
		private String name;
		private int age;
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
		@Override
		public String toString() {
			return "{name : " + name + ", age : " + age + "}";
		}
	}
}




// 执行结果
// before change user = {name : Scott, age : 5}
// name = Scott
// age = 5
// after change user = {name : Tom, age : 20}

面试官:恩,你的答案完全正确。age是一个基本类型变量,User、String都是引用类型变量,但是在调用change()方法之后,实参name和age都没有改变,而user对象改变了。那么可不可以说调用change()方法时,User对象是引用传递,age是值传递?可是String的传递方式又是什么呢?它的表现和age相同,但本身确是引用对象,这该如何解释呢?

:Java和C++不同,C++中有引用传递的函数调用方式,而Java中只有值传递

首先,值传递的官方解释是:

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

其次,引用传递的官方解释是:

引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

一句话概括值传递和引用传递的区别:

值传递是传递实参副本,函数修改不会影响实参;引用传递是传递实参地址,函数修改会影响实参。

面试官:恩,那你可不可以结合上面的程序题,解释一下这句话呢?

:好的。针对于上面程序的应用场景,在调用change()方法的时候,user、name、age三个变量都是值传递

其中,user对象是将引用拷贝了一份,引用是对象的地址,change()中对user的修改,并没有影响到这个地址,而是修改了对象属性。产生混淆的关键在于人们看到对象本身被函数修改了,就错误的认为这是引用传递。但我们区分值传递还是引用传递的关键在于实参是否被函数所修改,对于user对象来说地址才是实参!但如果你在change()方法中修改user引用的地址,即新创建一个新的user对象的话,就会看到main方法中的user并没有任何改变,也就反向印证了它实际上是值传递。

name变量自然也是将name引用拷贝一份传递给change()方法,根据值传递的定义,函数对这个副本的修改不会影响到实际参数,又因为String的final特性,name = “Tom”; 实际上就是修改了name的地址,因此,实际参数不会受函数修改的影响。

age其本身也是将数值拷贝一份传入change(),所以任何修改都不会影响到实参。所以我们说,在Java中只有值传递这一种参数传递方式。

面试官:恩,你的理解已经非常到位了,那额外问一句,对于上面的三种类型User、String、int,当输出结果如何时,才可以认定是引用传递呢?

:对于User对象,上面的代码实际上并不能很好的验证这个问题,反而给人一种对象即是实参的假象。实际上,如果调用change()方法后,重新new 一个User对象后赋值给user,即改变了变量的地址。在这种情况下,main中如果可以成功打印出新对象的话,才可以认定是引用传递。

String类型的name也是一样,如果在change()中 name = “Tom”; main中如果可以打印出 Tom 的话才可以认为是引用传递。

包括age变量,尽管它是一个基本类型变量,但在内存中,依然有地址和数据的区分,如果在change()中的修改,main中可以看到的话,才可以说是引用传递。

Java 中基于各种数据类型分析 == 和 equals 的区别

  1. equals()的底层就是==,所以需要比较内容而不是地址值的话就需要覆盖重写equals()和hashcode()

Java 中的数据类型,可分为两类:

  1. 基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean 它们之间的比较,应用双等号(==),比较的是它们的值。

  2. 复合数据类型(类)。
    当它们用双等号进行比较的时候,比较的是它们在内存中的存放地址,所以,除非是同一个 new 出来的对象,它们的比较后的结果为 true,否则比较后结果为 false。

也就是说

  • ==
    1. 如果比较的对象是基本数据类型,则比较值是否相等
    2. 如果比较的是引用数据类型,则比较的是对象的内存地址是否相等

因为Java只有值传递,所以对于==来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。

  • equals()
    用来比较两个对象的内容是否相等

int和Integer

  1. int 是基本数据类型,Integer 是 int 的包装类,也叫做复合数据类型。
  2. Integer变量必须实例化后才能使用,而int变量不需要
  3. Integer的默认值是null,int的默认值是0;
    即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。
  4. Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
// 装箱
// 自动根据数值创建对应的 Integer对象,这就是装箱
// 动装箱时编译器就是通过调用valueOf()将原始类型值转换成对象
Integer i =10;

// 拆箱
// 将自动将包装器类型转换为基本数据类型
// 自动拆箱的时候编译器自动调用的是Integer的intValue()方法

Integer i = 10;  //装箱
int n = i;   //拆箱 

基本数据类型和包装类型的比较

  • Byte,Short,Integer,Long,Character,Boolean都实现了常量池技术,范围都和Integer一样
  • float和double没有实现
public static void main(String[] args) {


            System.out.println("------------------一、int和Integer------------");


            System.out.println("--------------1、Integer对象使用new关键字生成-----------");

            Integer i = new Integer(100);
            Integer j = new Integer(100);
            System.out.println("i == j:" + (i == j));
            System.out.println("i.equals(j):" + (i.equals(j)));
            System.out.println("i.hashCode() == :" + i.hashCode());
            System.out.println("j.hashCode() == :" + j.hashCode());
            System.out.println("i,it's memory address:" + System.identityHashCode(i));
            System.out.println("j,it's memory address:" + System.identityHashCode(j));

            // false
            // true
            // 100
            // 100
            // 625576447
            // 1560911714

            // 正如上文提到的那样,复合数据类型使用双等号的时候是比较其在内存中的地址是否相同。
            // 一般而言,Object 的 hashCode()默认是返回内存地址的,
            //          在本例中直接输出对象的 hashCode 可以发现两者是一致的,那为什么==比较结果为 false呢?
            //          原因在于hashCode()可以重写,所以 hashCode()不能代表对象在内存的地址。
            //
            // System.identityHashCode(Object)方法可以得到对象的内存地址结果
            //          (严格意义上来讲,System.identityHashCode 的返回值和内存地址不相等的,
            //          该值是内存地址通过算法换算的一个整数值),不管该对象的类是否重写了 hashCode()方法。

            // Integer 类中关于 equals()方法和 hashCode()方法进行了重写,
            //          所以如果想比对内存地址的不同,需要使用System.identityHashCode(Object)方法。


            System.out.println("--------------2、表面上不是 new 关键字生成的 Integer 对象-----------");

            Integer a = 100;
            Integer b = 100;
            System.out.println("a == b:" + (a == b));
            System.out.println("a.equals(b):" + (a.equals(b)));
            System.out.println("a.hashCode():" + a.hashCode());
            System.out.println("b.hashCode():" + b.hashCode());
            System.out.println("a,it's memory address:" + System.identityHashCode(a));
            System.out.println("b,it's memory address:" + System.identityHashCode(b));

//            a == b:true
//            a.equals(b):true
//            a.hashCode():100
//            b.hashCode():100
//            a,it's memory address:939047783
//            b,it's memory address:939047783

            System.out.println("--------------2.1、这里就不得不提出另一种情况-----------");
            Integer ii = 128;
            Integer jj = 128;
            System.out.println("ii == jj:" + (ii == jj));
            System.out.println("ii.equals(jj):" + (ii.equals(jj)));
            System.out.println("ii.hashCode():" + ii.hashCode());
            System.out.println("jj.hashCode():" + jj.hashCode());
            System.out.println("ii,it's memory address:" + System.identityHashCode(ii));
            System.out.println("jj,it's memory address:" + System.identityHashCode(jj));


//            ii == jj:false
//            ii.equals(jj):true
//            ii.hashCode():128
//            jj.hashCode():128
//            ii,it's memory address:1237514926
//            jj,it's memory address:548246552

//            对于两个非 new 生成的 Integer 对象,进行比较时,如果两个变量的值在区间 -128 到 127 之间,
//                      则比较结果为 true,如果两个变量的值不在此区间,则比较结果为 false。
//                      通过打印出来的地址可以看出来,当不在指定区间范围时,实际上是两个不同的对象。
//
//             具体原因: Java 在编译 Integer i = 100 ;时,会翻译成为 Integer i = Integer.valueOf(100)。
//                      而 Java API 中对 Integer 类型的valueOf 的定义如下,对于-128 到 127 之间的数,会存储在缓存中,
//                      Integer i = 127 时,会直接从缓存中获取,下次再 Integer j = 127时,同样从缓存中取,而不会 new 个新对象。


            System.out.println("--------------3、两个 int 变量比较-----------");

            int c = 100;
            int d = 100;
            System.out.println("c == d:" + (c == d));
            System.out.println("c,it's memory address:" + System.identityHashCode(c));
            System.out.println("d,it's memory address:" + System.identityHashCode(d));

            //c == d:true
            //c,it's memory address:939047783
            //d,it's memory address:939047783

            // 对于这种简单数据类型,== 比较符就是比较它们的值大小


            System.out.println("--------------4、new 生成的 Integer 对象和 int 变量比较----------");


            Integer e = new Integer(100);
            int f = 100;
            System.out.println("e == f:" + (e == f));
            System.out.println("e.hashCode():" + e.hashCode());
            System.out.println("e,it's memory address:" + System.identityHashCode(e));
            System.out.println("f,it's memory address:" + System.identityHashCode(f));


            //e == f:true
            //e.hashCode():100
            //e,it's memory address:835648992
            //f,it's memory address:939047783

            // 基本数据类型 int 和它的包装类 Integer 比较时,Java 会自动拆包装为 int(将复合数据类型转化为基本数据类型),
            //          然后进行比较,实际上就变为两个 int 变量的比较。即使打印出来的地址不同,但是比较结果仍为 true,
            //          主要原因是因为不是通过比较内存地址进行判断的。


            System.out.println("--------------5、非 new 生成的 Integer 对象和 int 变量比较----------");

            int k = 100;
            Integer l = 100;
            System.out.println("k == l:" + (k == l));
            System.out.println("k,it's memory address:" + System.identityHashCode(k));
            System.out.println("l,it's memory address:" + System.identityHashCode(l));


            int m = 128;
            Integer n = 128;
            System.out.println("m == n:" + (m == n));
            System.out.println("m,it's memory address:" + System.identityHashCode(m));
            System.out.println("n,it's memory address:" + System.identityHashCode(n));


//            k == l:true
//            k,it's memory address:939047783
//            l,it's memory address:939047783
//            m == n:true
//            m,it's memory address:1134517053
//            n,it's memory address:492228202


            // 比较结果都为 true,因为同 4 一样,由于自动拆箱的特性,其实是进行值的比较,所以结果为 true。
            //          接着分析打印的内存地址,当在[-128,127]区间范围内时,Integer 数组(参考 Integer 类中的 IntegerCache)是存放在常量池中的,
            //          而 int 变量同样也是,所以值相等时,内存地址一致。


            System.out.println("--------------6、非 new 生成的 Integer 对象和 new Integer()生成的对象----------");


            Integer o = new Integer(100);
            Integer p = 100;
            System.out.print(o == p);

            // false
            // 因为非 new 生成的 Integer 变量指向的是 Java 常量池中的对象,而 new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同。


            System.out.println("--------------7、面试题----------");

            Integer i1 = 125;
            Integer i2 = 125;
            Integer i3 = 0;
            Integer i4 = new Integer(127);
            Integer i5 = new Integer(127);
            Integer i6 = new Integer(0);

            System.out.println("i1==i2:\t" + (i1 == i2));
            System.out.println("i1==i2+i3:\t" + (i1 == i2 + i3));
            System.out.println("i4==i5:\t" + (i4 == i5));
            System.out.println("i4==i5+i6:\t" + (i4 == i5 + i6));

            i3 = 5;
            Integer i7 = 130;
            System.out.println("i7==i2+i3:\t" + (i7 == i2 + i3));


//            i1==i2:	true
//            i1==i2+i3:	true
//            i4==i5:	false
//            i4==i5+i6:	true
//            i7==i2+i3:	true

            // 对于 i1 == i2 + i3 、 i4 == i5 + i6 和 i7 == i2 + i3 结果为 true,
            //          是因为,Java 的数学计算是在内存栈里操作的,Java 会对 i5、i6 进行拆箱操作,其实比较的是基本类型(127=127+0),
            //          他们的值相同,因此结果为 true。
            //          对 i2+i3 来说,结果是在内存栈中(同 int 基本类型一样),所以不管是与 i1 还是 i7 比较,返回结果都为 true。


    }
// float与double一致
public static void main(String[] args) {


            System.out.println("------------------二、double 和 Double------------");


            System.out.println("--------------1、new 生成的两个 Double 对象比较-----------");

            Double i = new Double(100.0);
            Double j = new Double(100.0);

            System.out.println("i == j:" + (i == j));
            System.out.println("i.equals(j):" + (i.equals(j)));
            System.out.println("i.hashCode():" + i.hashCode());
            System.out.println("j.hashCode():" + j.hashCode());
            System.out.println("i,it's memory address:" + System.identityHashCode(i));
            System.out.println("j,it's memory address:" + System.identityHashCode(j));

            //i == j:false
            //i.equals(j):true
            //i.hashCode():1079574528
            //j.hashCode():1079574528
            //i,it's memory address:868693306
            //j,it's memory address:1746572565

            // Double 类源码可以发现,也重写了 equals 方法和 hashCode 方法




            System.out.println("--------------2、表面上非 new 生成的 Double 对象比较-----------");

            Double a = 100.0;
            Double b = 100.0;

            System.out.println("a == b:" + (a == b));
            System.out.println("a.equals(b):" + (a.equals(b)));
            System.out.println("a.hashCode():" + a.hashCode());
            System.out.println("b.hashCode():" + b.hashCode());
            System.out.println("a,it's memory address:" + System.identityHashCode(a));
            System.out.println("b,it's memory address:" + System.identityHashCode(b));

            //a == b:false
            //a.equals(b):true
            //a.hashCode():1079574528
            //b.hashCode():1079574528
            //a,it's memory address:989110044
            //b,it's memory address:424058530

            // 自动装箱,解析为 Double i = new Double(100.0);因此实际上还是两个不同的对象。


            System.out.println("--------------3、new 生成的 Double 对象和 double 变量比较-----------");
            Double c = 100.0;
            double d = 100.0;

            System.out.println("c == d:" + (c == d));
            System.out.println("c.equals(d):" + (c.equals(d)));
            System.out.println("c,it's memory address:" + System.identityHashCode(c));
            System.out.println("d,it's memory address:" + System.identityHashCode(d));

            //c == d:true
            //c.equals(d):true
            //c,it's memory address:321001045
            //d,it's memory address:791452441
            // 自动拆箱,转换为 double 变量进行值比较。

}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐