Java基础一基本数据类型和String
Java中的默认类型:整数类型是 int 、浮点类型是 double 。
虽然float占4个字节,但是采用科学技术法,取值范围远远大于8个字节的long。

数据类型转换
Java程序中要求参与的计算的数据,必须要保证数据类型的一致性,如果数据类型不一致将发生类型的转换。
自动转换
当表达式两边数据类型不一致时,取值范围小的数据或者变量,可以直接赋值给取值范围大的变量
- 不需要进行代码的特殊处理
- byte,short,char数据,只要参加运算,就会自动转换成int类型
- 表达式最终的数据类型,一定是表达式中取值范围最大的类型
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 的区别
- equals()的底层就是==,所以需要比较内容而不是地址值的话就需要覆盖重写equals()和hashcode()
Java 中的数据类型,可分为两类:
-
基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean 它们之间的比较,应用双等号(==),比较的是它们的值。
-
复合数据类型(类)。
当它们用双等号进行比较的时候,比较的是它们在内存中的存放地址,所以,除非是同一个 new 出来的对象,它们的比较后的结果为 true,否则比较后结果为 false。
也就是说
- ==
- 如果比较的对象是基本数据类型,则比较值是否相等
- 如果比较的是引用数据类型,则比较的是对象的内存地址是否相等
因为Java只有值传递,所以对于==来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
- equals()
用来比较两个对象的内容是否相等
int和Integer
- int 是基本数据类型,Integer 是 int 的包装类,也叫做复合数据类型。
- Integer变量必须实例化后才能使用,而int变量不需要
- Integer的默认值是null,int的默认值是0;
即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。 - 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 变量进行值比较。
}
更多推荐




所有评论(0)