文章目录
- 1️⃣String类
- 2️⃣Object类
- 3️⃣包装类
- 1.基本类型和包装类
- 2.装箱和拆箱
- 3.自动拆装箱
- 4.包装类和String
- 5.编译期的自动拆装箱
1️⃣String类
理解字符串的不可变性。
源码:
- 内部存储数据的
value
字符数组对外不可见,外部无法直接使用;且被final
修饰,无法修改内容,字符串字面值直接赋值常量池。 - 被
final
修饰的类,无法继承,确保所有JDK程序员使用的都是同一个类,保证规范性。
2️⃣Object类
1.Object
类是所有类的默认父类(不需要使用extends
显示继承),因此Object
类的所有方法(private
除外),所有类的对象都能使用。
我们来看看Object
源码的结构:
toString()方法
能将任意对象转为字符串输出,打印对象内容,System.out.println()
接收任意对象并输出,默认调用的就是toString()
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Test {
public static void main(String[] args){
Student student = new Student("张三",40);
System.out.println(student);
}
}
想要输出具体内容,就需要覆写toString
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
输出结果:
equals()方法
想要比较两个对象是否相等(一般比较属性),需覆写equals
方法。
默认的==
会比较当前对象(this表示当前对象的引用)与传入的对象地址是否相等。
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Test {
public static void main(String[] args){
Student student = new Student("张三",40);
Student student1 = student;
Student student2 = new Student("张三",40);
System.out.println(student.equals(student1));
System.out.println(student.equals(student2));
}
}
//输出结果;
true
false
此时 Student 类默认使用的 Object 提供的 equals 方法,student 和 student1 都是对象的引用,指向同一片内存空间,所以返回 true 虽然 student 和 student2 的属性值相等,但是他俩的地址不同,所以返回 false。
若需要让 Student 类的 equals 按属性值比较,得要覆写 equals 方法:
// 当前对象和传入的obj相比
public boolean equals(Object obj) {
// 1.判断obj是否为空
if (obj == null) {
return false;
}
// 2.判断obj是否就是自己
if (obj == this) {
return true;
}
// 3.判断obj是否是Student类的对象
//instanceof可以判定一个引用是否是某个类的实例
if (!(obj instanceof Student)) {
return false;
}
// 4.obj一定是Student类的对象而且与当前对象不是一个地址
// 向下转型还原为Student,比较当前对象的属性和传入对象的属性
Student stu = (Student) obj;
return this.name.equals(stu.name) && this.age == stu.age;
}
2.Object
不仅是所有类的父类,而且只要是引用数据类型,都可以向上转型变为Object
,Object
类型可以接收所有引用数据类型,包括数组和接口。
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
interface IMessage{
void print();
}
class MessageImpl implements IMessage{
@Override
public void print() {
System.out.println("hello");
}
}
public class ObjectTest {
public static void main(String[] args){
//自定义类型
Object obj1 = new Student("张三",40);
//字符串
Object obj2 = "hello";
//顺序表
Object obj3 = new ArrayList<>();
//数组引用
int[] data = new int[]{3,2,5,8,4,7,6,9};
//接口引用
IMessage msg = new MessageImpl();
Object obj4 = data;
Object obj5 = msg;
int[] ret = (int[])obj4;
IMessage imsg = (IMessage) obj5;
System.out.println(Arrays.toString(ret));
imsg.print();
}
}
//输出结果:
[3, 2, 5, 8, 4, 7, 6, 9]
hello
Object
是引用数据类型的最高参数统一化,如果设计的方法需要接收所有引用类型,那就把类型声明为Object
。
3️⃣包装类
1.基本类型和包装类
Object
引用可以指向任意类型的对象,但有例外出现了,8 种基本数据类型不是对象,那岂不是刚才的泛型机制要失效了?
实际上也确实如此,为了解决这个问题,java 引入了一类特殊的类,即这 8 种基本数据类型的包装类,在使用过程中,会将类似 int
这样的值包装到一个对象中去。
基本数据 | 类型包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
基本就是类型的首字母大写,除了Integer
和 Character
。
自己实现的包装类
class MyInterger{
//保存具体的整形值
private int data;
public MyInterger(int data){
this.data = data;
}
public int intValue(){
return this.data;
}
}
public class IntDemo {
public static void main(String[] args) {
//使用JDK内置的包装类
//将int->类
Integer demo = new Integer(10);
int data = demo.intValue() + 1;
System.out.println(data);
//自己实现
MyInterger demo = new MyInterger(10);
int data = demo.intValue() + 1;
System.out.println(data);
}
}
//输出结果:
11
2.装箱和拆箱
int i = 10;
// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);
// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();
3.自动拆装箱
可以看到在使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者的负担,java 提供了自动机制。
int i = 10;
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱
int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱
在用法上上,包装类和基本类型没啥不同,但是在一些细节处有区别:
🅰️对于基本类型,其默认值是数值0
,对于包装类而言,默认值是null
。
public class IntDemo {
private int a;
private Integer b;
public static void main(String[] args) {
IntDemo demo = new IntDemo();
System.out.println(demo.a);
System.out.println(demo.b);
}
}
//输出结果:
0
null
那么到底什么时候用基本类型,什么时候用包装类呢?
阿里编码规约:
- 在类中定义成员变量,必须使用包装类声明。
原因:在开发一个银行卡系统中,肯定会有用户的卡号和余额(浮点数)double balance;
默认值是0.0,那么是正常使用下的余额为0.0,还是用户已经注销了设置的呢?Double balance;
默认是null
,此时和默认值0.0就不同了,便可以将余额清0和帐号注销两种情况区分开来。
将来把类的成员变量存储到数据库中,默认值经常造成干扰。利用null
来判断是否存在数值。 - 方法中大量进行算术运算时,使用基本类型。
🅱️关于包装类的对象比较使用eqauls!!!
包装类使用==
是比较两个包装类对象的地址是否相等!!需要比较值用eqauls
。
对于包装类而言(以int
为例),自动装箱时,JVM会创建一个缓存处理。
对于[-128,127]的取值,Integer
会缓存对象(String常量池)
例如:
4.包装类和String
🅰️包装类->String
Integer i1 = 10;
String str1 = String.valueOf(i1);
System.out.println(str1);
//输出结果:
10
🅱️String->包装类
String str2 = "123";
Integer i2 = Integer.parseInt(str2);
System.out.println(i2);
//输出结果:
123
注意字符串中不要包含处数字外的其他字符,否则会报异常
5.编译期的自动拆装箱
可以使用反编译工具来查看下自动装箱和自动拆箱过程,并且看到这个过程是发生在编译期间的。
javap -c 类名称
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 10
2: istore_1
3: iload_1
4: invokestatic #2 // Method java/lang/Integer.valueOf:
(I)Ljava/lang/Integer;
7: astore_2
8: iload_1
9: invokestatic #2 // Method java/lang/Integer.valueOf:
(I)Ljava/lang/Integer;
12: astore_3
13: aload_2
14: invokevirtual #3 // Method java/lang/Integer.intValue:()I
17: istore 4
19: aload_2
20: invokevirtual #3 // Method java/lang/Integer.intValue:()I
23: istore 5
25: return
}