Java-02: Basic
数据类型
基本数据类型
- 整数类型:byte/8, short/16, int/32, long/64
- 浮点类型:float/32, double/64
- 字符类型:char/16
- 布尔类型:boolean/1
包装类型
包装类型让基本数据类型拥有了对象的特性,方便它们参与到面向对象的编程中,尤其是在集合框架、泛型以及需要表示 null 值时。
Integer i = 10; // 装箱
int j = i; // 拆箱
// 集合
ArrayList<Integer> numbers = new ArrayList<>(); // 不能使用ArrayList<int>
numbers.add(1);
// 泛型
Map<String, Integer> map = new HashMap<>(); // 键值对都要使用包装类型
// 方法参数
public void print(Integer i) {
System.out.println(i);
}
// 返回值
public Integer add(int a, int b) {
return a + b;
}
// 表示 null 值
Integer i = null;
// 与字符串互相转换
String str = "123";
Integer i = Integer.parseInt(str);
String str2 = i.toString();
// 网络编程和IO操作
Socket socket = new Socket("localhost", 8080);
File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file);
// ORM
User user = userDao.findById(1);
缓存池
缓存池:为了优化性能和节省内存,Java 为一些常用的值(如 -128 到 127)提供了一个缓存池,当需要这些值时,可以直接从缓存池中获取,而不是重新创建。
缓存池范围:
- Byte、Short、Integer、Long:默认缓存范围是 -128 到 127。
- Character:缓存范围是 0 到 127(ASCII 码)。
- Boolean:缓存 true 和 false 两个实例。
public class CachePoolExample {
public static void main(String[] args) {
// Integer 缓存池示例
Integer i1 = 100; // 自动装箱,100在-128到127范围内
Integer i2 = 100; // 自动装箱,从缓存池中获取
Integer i3 = 200; // 自动装箱,200超出-128到127范围,会创建新对象
Integer i4 = 200; // 自动装箱,200超出范围,会创建新对象
System.out.println("i1 == i2: " + (i1 == i2)); // true,因为都是从缓存池中获取的同一个对象
System.out.println("i3 == i4: " + (i3 == i4)); // false,因为创建了两个不同的对象
// Boolean 缓存池示例
Boolean b1 = true;
Boolean b2 = true;
System.out.println("b1 == b2: " + (b1 == b2)); // true,Boolean缓存了true和false
// Character 缓存池示例
Character c1 = 'a'; // 'a'的ASCII码是97,在0到127范围内
Character c2 = 'a';
Character c3 = '中'; // '中'的ASCII码超过127
Character c4 = '中';
System.out.println("c1 == c2: " + (c1 == c2)); // true
System.out.println("c3 == c4: " + (c3 == c4)); // false
// 正确比较包装类型的值应该使用 equals() 方法
System.out.println("i3.equals(i4): " + i3.equals(i4)); // true
}
}
new Integer(123) 与 Integer.valueOf(123) 的区别: new Integer(123) 会创建一个新的对象,而 Integer.valueOf(123) 会从缓存池中获取一个对象
javaInteger i1 = new Integer(123); Integer i2 = Integer.valueOf(123); System.out.println(i1 == i2); // false Integer i3 = Integer.valueOf(123); Integer i4 = Integer.valueOf(123); System.out.println(i3 == i4); // true
String
public final class String // 不可继承
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; // 不可变字符序列
}
不可变性
- 优点:
- 可以缓存 hash 值
- String Pool 的需要
- 安全性
- 线程安全
StringBuffer 和 StringBuilder
- StringBuffer 是线程安全的,通过 synchronized 关键字实现,StringBuilder 是线程不安全的
- StringBuffer 的性能比 StringBuilder 差
- StringBuffer 的 API 比 StringBuilder 多
String.intern()
- 将字符串常量池中的字符串添加到 String Pool 中
- 如果 String Pool 中已经存在该字符串,则返回 String Pool 中的字符串
- 如果 String Pool 中不存在该字符串,则将该字符串添加到 String Pool 中,并返回 String Pool 中的字符串
String s1 = "hello";
String s2 = new String("hello");
String s3 = s2.intern();
String s4 = "hello";
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // true
String.format()
- 格式化字符串
String s = String.format("Hello %s", "World");
System.out.println(s); // Hello World
运算
参数传递
java 参数传递是使用的值传递,student 存储的是对象地址,传入 func 方法时,传递的是对象地址的副本,因此在方法中改变指针引用的对象,那么两个指针将指向不同的对象,因此不会改变原始对象。
javapublic class Student { name; Student(String name) { this.name = name; } String getName() { return this.name; } void setName(String name) { this.name = name; } } public class PassByValueExample { public static void main(String[] args) { Student student = new Student("A"); System.out.println(student.getName()); // A func(student); System.out.println(student.getName()); // A } private static void func(Student student) { System.out.println(student.getName()); // A Student student = new Student("B"); System.out.println(student.getName()); // B } }
但是如果改变的是对象的属性,那么就会改变原始对象,因为修改的是同一地址指向的内容。
javaclass PassByValueExample { public static void main(String[] args) { Student student = new Student("A"); System.out.println(student.getName()); // A func(student); System.out.println(student.getName()); // B } private static void func(Student student) { System.out.println(student.getName()); // A student.setName("B"); System.out.println(student.getName()); // B } }
float 和 double
1.1 字面量属于 double 类型,不能将 1.1 直接复制给 float,因为这是向下转型,java 不支持隐式向下转型。
1.1f 才是 floatl 类型
javafloat f = 1.1f
隐式类型转换
字面量 1 是 int 类型,所以不能隐式转型为 short
javashort s = 1; s = s + 1; // 错误
但是可以使用+=运算符,因为+=运算符会进行隐式类型转换
javashort s = 1; s += 1; // 正确 // 相当于 s = (short) (s + 1);
switch
switch (expression) {
case value1:
// code block
break;
case value2:
// code block
break;
}
访问权限
修饰符:public、protected、private,如果不加修饰符,表示默认访问权限,即包内可见。
类可见,表示该类可以被其他类用于创建对象。
成员可见,表示该成员可以被其他类访问。
protected 表示该成员对于子类可见,但是对于类没有意义。
子类的访问级别不能高于父类的访问级别。
字段绝对不能是公有的,因为这样会丢失字段修改行为的控制。通常使用 private 修饰,并提供 public 的 getter 和 setter 方法。
javapublic class Student { private int id; public int getId() { return id + ""; } public void setId(String id) { this.id = Integer.valueOf(id); } }
抽象类和接口
抽象类:抽象类不能被实例化,但是可以被继承。使用 abstract 修饰。
javapublic abstract class Animal { public abstract void eat(); public void sleep() { System.out.println("sleep"); } } public class Dog extends Animal { @Override public void eat() { System.out.println("eat"); } } Animal animal = new Dog(); animal.eat();
接口:接口不能被实例化,但是可以被实现。使用 interface 修饰。接口中的字段和方法默认是 public abstract 的。
javapublic interface Animal { void eat(); }
javapublic class Dog implements Animal { @Override public void eat() { System.out.println("eat"); } } Animal animal = new Dog(); animal.eat();
比较
区别 | 抽象类 | 接口 |
---|---|---|
关系 | IS-A(里氏替换原则) | LIKE-A |
实现 | 单继承 | 多继承 |
字段 | 没有限制 | static final |
成员权限 | 没有限制 | public abstract |
方法 | 可以有实现 | 只能有抽象方法 |
构造函数 | 可以有构造函数 | 不能有构造函数 |
选择(接口优先级高于抽象类)
接口:1.不相关的类都实现一些方法,2.使用多重继承
抽象类:1.相关类共享代码 2.需要能控制继承来的成员的访问权限 3.需要继承非静态和非常量字段。
Super
使用 super 调用父类的构造函数,委托父类完成初始化工作
javapublic class Animal { protected int age; public Animal(int age) { this.age = age; } public void eat() { System.out.println("eat"); } } public class Dog extends Animal { private String name; public Dog(int age, String name) { super(age); this.name = name; } @Override public void eat() { super.eat(); System.out.println("dog eat") } } Dog dog = new Dog(1); dog.eat(); // eat dog eat
重写与重载
重写(使用@Override注解)
子类方法访问权限不能低于父类方法访问权限
子类方法返回类型必须是父类方法返回类型的子类
重载
存在同一个类,方法名相同,参数类型、个数、顺序至少一个不同
重载与返回值无关,只与方法签名有关
通用方法
public final native Class<?> getClass() // 获取对象的类
public native int hashCode() // 获取对象的哈希码
public boolean equals(Object obj) // 判断对象是否相等:自反性、对称性、传递性、一致性、非空性
protected native Object clone() throws CloneNotSupportedException // 克隆对象
public String toString() // 返回对象的字符串表示
public final native void notify() // 唤醒等待的线程
public final native void notifyAll() // 唤醒所有等待的线程
public final native void wait(long timeout) throws InterruptedException // 等待指定时间
public final void wait(long timeout, int nanos) throws InterruptedException // 等待指定时间
public final void wait() throws InterruptedException // 等待
protected void finalize() throws Throwable {} // 垃圾回收
浅拷贝:拷贝对象的引用,如果对象的属性是基本类型,则拷贝值,如果对象的属性是引用类型,则拷贝引用。
javapublic class ShallowCopy { public static void main(String[] args) { Student student = new Student("A"); Student student2 = student; student2.setName("B"); System.out.println(student.getName()); // B } }
深拷贝:拷贝对象和原始对象的引用类型引用不同的对象。
javapublic class Student implements Cloneable { private int id; public Student(int id) { this.id = id; } public void setId(int id) { this.id = id; } public int getId() { return id; } @Override protected Object clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); student.id = this.id; return student; } } Student student = new Student(1); Student student2 = student.clone(); student2.setId(2); System.out.println(student.getId()); // 1 System.out.println(student2.getId()); // 2
clone的替代,拷贝构造函数或者拷贝工厂
javapublic class Student { private int id; public Student(int id) { this.id = id; } public Student(Student student) { this.id = student.id; } public void setId(int id) { this.id = id; } public int getId() { return id; } } public class StudentFactory { public static Student createStudent(int id) { return new Student(id); } } Student student = StudentFactory.createStudent(1); System.out.println(student.getId()); // 1
关键字
final:
修饰类:类不能被继承
修饰方法:方法不能被重写
修饰变量:变量不能被修改
修饰引用:引用不能被修改,但是引用指向的对象可以被修改
static
静态变量:也称类变量,属于类,不属于对象,所有对象共享
实例变量:也称成员变量,属于对象,每个对象都有一份
javapublic class Student { private int id; // 实例变量 private static int count = 0; // 静态变量 public Student(int id) { this.id = id; count++; } }
静态方法:类加载的时候就存在,不需要实例化就可以调用,必须有实现,不能是抽象方法。且只能访问静态变量和静态方法,不能有this和super关键字。
静态代码块:在类初始化的时候运行一次。
静态内部类:非静态内部类依赖于外部类,而静态内部类不需要依赖外部类,可以独立存在。但是只能访问外部类的非静态变量和方法。
静态导包:不再指明包名,直接使用静态方法和变量,但是可读性较差。
初始化顺序:静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。最后才是构造函数的初始化。
存在继承关系时,初始化顺序为:
- 父类静态变量和静态语句块
- 子类静态变量和静态语句块
- 父类实例变量和普通语句块
- 父类构造函数
- 子类实例变量和普通语句块
- 子类构造函数
反射
每一个类都有Class对象,Class对象保存了类的一些信息,如类名、方法、字段等。当编译一个类时,会生成一个Class对象,并保存在.class文件中。类加载相当于class对象的加载,类再第一次使用的似乎才会动态加载到JVM中,可以使用
Class.forName("com.example.Student")
来控制加载时机,该方法会返回与参数中指定的对象相应的Class对象的引用。Class和java.lang.reflect提供了反射的支持,java.lang.reflect包提供了Field、Method和Constructor类,可以用来获取类的信息。
Field:表示类的成员变量,可以使用get和set方法来获取和设置字段的值。
Method:表示类的方法,可以使用invoke方法来调用方法。
Constructor:表示类的构造函数,可以使用newInstance方法来创建对象。
异常
受检异常:用try-catch来处理,并从异常中恢复。
非受检异常:程序运行时错误,程序崩溃。
泛型
泛型类:使用
<T>
来表示泛型,T可以表示任何类型。javapublic class Generic<T> { private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } }
JRE和JDK
JRE:Java Runtime Environment,Java运行环境,包含了JVM和Java核心类库。
JDK:Java Development Kit,Java开发工具包,包含了JRE和开发工具。