java基础-知识点(一)
1.数据类型
八个基本类型
1、整数类型
byte字节型:1 字节,范围 -128 ~ 127short短整型:2 字节,-32768 ~ 32767int整型:4 字节,-2147483648 ~ 2147483647(默认整数)long长整型:8 字节,很大范围
2、浮点类型
float单精度:4 字节double双精度:8 字节(默认小数)
3、字符类型
char字符:2 字节,存单个汉字 / 字母
4、布尔类型
boolean布尔:1 位,只有true / false
5、自动装箱、自动拆箱
1.自动装箱
基本类型 -> 包装类
int a = 10;
Integer b = a; // 自动装箱
2.自动拆箱
包装类 -> 基本类型
Integer i = 20;
int num = i; // 自动拆箱
总结
- 基本变包装 = 装箱
- 包装变基本 = 拆箱
- 不用手动转换,Java 自动完成
2.缓存池
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
- new Integer(123) 每次都会新建一个对象
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在 Java 8 中,Integer 缓存池的大小默认为 -128~127。
编译器会在缓冲池范围内的基本类型自动装箱过程调用 valueOf() 方法,因此多个 Integer 实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。
Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true
如果在缓冲池之外:
Integer m = 323;
Integer n = 323;
System.out.println(m == n); // false
3.String
概览
String 被声明为 final,因此它不可被继承。
内部使用 char 数组存储数据,该数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
不可变的好处
1.字符串常量池复用,节省内存
String 不可改,所以 JVM 可以把字符串放进常量池共享
"abc"多处使用,永远同一个对象- 不用重复创建,极大节约堆内存
2.线程安全,多线程随便用
不可变 = 永远不会被修改
多个线程同时读取 String,天然线程安全,不需要加锁
3.HashMap 哈希值不变,可以作为Key
String 哈希码 hashCode() 只计算一次缓存
- 如果可变,修改字符串后哈希值变了
- HashMap 找不到元素、链表混乱、数据错乱
所以 Map 键绝大多数都用 String
4.字符串拼接安全,不会混乱
String 一旦创建终身不变,修改只会生成新对象
不会出现同一个字符串多处引用互相影响
5.支持字符串常量池、intern () 机制
只有不可变,才能池化复用
可变字符串绝对不能放进常量池
4.String--StringBuffer--StringBuilder
1.可变性
- String 不可变
- StringBuffer 和 StringBuilder 可变
2.线程安全
- String 不可变,因此是线程安全的
- StringBuilder 不是线程安全的
- StringBuffer 是线程安全的,内部使用 synchronized 进行同步
3.StringBuilder和StringBuffer的区别
1.线程安全
- StringBuffer:方法加了
synchronized→ 线程安全,多线程拼接安全 - StringBuilder:没有锁 → 线程不安全,单线程更快
2.执行效率
- StringBuilder 速度 远快于 StringBuffer
- 加锁开销很大,并发不高没必要用 StringBuffer
3.继承关系
两者都继承 AbstractStringBuilder
4.使用场景
- 单线程、循环拼接字符串 → StringBuilder(99% 场景用它)
- 多线程共享拼接、并发操作 → StringBuffer
5.共同点
- 都是可变字符串
- 频繁拼接不会产生大量垃圾对象
- 都有 append、insert、reverse 等方法
5.运算
参数传递
Java 永远只有一种传递方式:值传递!没有引用传递!
- 值传递:把变量副本传进去
- 引用传递:直接传变量本身地址,改外面跟着变
public static void changeNum(int a){
a = 100;
}
int x = 10;
changeNum(x);
System.out.println(x); // 还是10
float 与 double
1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。Java 不能隐式执行向下转型,因为这会使得精度降低。
// float f = 1.1;
1.1f 字面量才是 float 类型。
float f = 1.1f;
隐式类型转换
因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型下转型为 short 类型。
short s1 = 1;
// s1 = s1 + 1;
但是使用 += 运算符可以执行隐式类型转换。
s1 += 1;
// 上面的语句相当于将 s1 + 1 的计算结果进行了向下转型
s1 = (short) (s1 + 1);
switch
从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。
String s = "a";
switch (s) {
case "a":
System.out.println("aaa");
break;
case "b":
System.out.println("bbb");
break;
}
switch 不支持 long,是因为 switch 的设计初衷是对那些只有少数的几个值进行等值判断,如果值过于复杂,那么还是用 if 比较合适。
6.继承
访问权限
Java 中有三个访问权限修饰符: private、protected 以及 public,如果不加访问修饰符,表示包级可见。
1.private 私有的
- 只能本类内部访问
- 类外、子类、同包都不能用
- 封装属性常用
private int age;
2.protected 受保护的
- 同包下:任意类都能访问
- 不同包:只有子类能继承访问
- 跨包无关类:不能访问
3.public 公共的
- 任何地方、任何包、任何类都能访问
- 无限制
| 修饰符 | 本类 | 同包 | 不同包子类 | 不同包无关类 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| default | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
极简总结
- private 自己用
- default 同家用
- protected 子孙用
- public 全世界用
抽象类与接口
1.抽象类
抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。
抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。
里面可以有:抽象方法 + 普通方法 + 成员变量
abstract class Animal{
// 抽象方法:没有方法体
public abstract void eat();
// 普通普通方法
public void sleep(){
System.out.println("睡觉");
}
}
- 抽象方法
public abstract void run();- 没有 {} 方法体
- 子类必须重写实现
- 核心特点
- 抽象类不能实例化(不能 new)
- 有抽象方法的类,必须是抽象类
- 子类继承抽象类:
- 要么重写所有抽象方法
- 要么子类自己也加上
abstract
- 可以有构造方法(给子类初始化用)
- 可以有普通变量、静态方法、普通方法
public abstract class AbstractClassExample {
protected int x;
private int y;
public abstract void func1();
public void func2() {
System.out.println("func2");
}
}
2.接口
接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。
从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
如果需要在接口有默认的方法实现必须在加关键字default或者static(java8之后引用)。
接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。
接口的字段默认都是 static 和 final 的。
- 接口核心特点
- 不能 new 对象
- 一个类可以实现多个接口(多实现)
- 接口可以继承多个接口
- 接口里变量:默认
public static final常量,必须初始化 - 没有构造方法
- JDK8 前后区别
- JDK 8 之前全是抽象方法,没有方法体
- JDK 8 及以后新增:
default普通默认方法(有方法体,子类可不重写)static静态方法
3.抽象类 vs 接口
- 继承数量
- 抽象类:单继承
- 接口:多实现
- 构造方法
- 抽象类:有构造
- 接口:无构造
- 成员变量
- 抽象类:普通成员变量
- 接口:只能常量
public static final
- 方法
- 抽象类:普通方法、抽象方法都行
- 接口:抽象、default、static
- 关系
- 类继承抽象类
extends - 类实现接口
implements
- 类继承抽象类
super
super 代表父类对象、父类引用
三种用法
1.super () 调用父类构造方法
- 必须写在方法第一行
- 子类构造默认自动
super()调用父类空参构造 - 父类没有空参构造,子类必须手动 super (参数)
public Son(){
super(); // 调用父类无参构造
}
2.super. 属性
调用父类成员变量
子类和父类重名变量时,区分父子
super.name;
3.super. 方法 ()
调用父类普通方法
子类重写方法后,想调用父类原来逻辑
super.show();
核心规则
- 子类构造默认第一行隐藏 super ()
- super () 和 this () 不能同时存在
- 只能在非静态方法里用 super
- 不能在 static 方法中使用 super /this
高频坑
- 子类重写父类方法 →
super.方法()才会执行父类逻辑 - 成员变量重名:
this子类,super父类 - 父类 private 属性,子类用不了 super
重写与重载
1.重写
存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
为了满足里式替换原则,重写有以下两个限制:
- 子类方法的访问权限必须大于等于父类方法;
- 子类方法的返回类型必须是父类方法返回类型或为其子类型。
使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。
2.重载
存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
应该注意的是,返回值不同,其它都相同不算是重载。
7.Object通用方法
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 {}
equals()
1. 等价关系
(一)自反性
x.equals(x); // true
(二)对称性
x.equals(y) == y.equals(x); // true
(三)传递性
if (x.equals(y) && y.equals(z))
x.equals(z); // true;
(四)一致性
多次调用 equals() 方法结果不变
x.equals(y) == x.equals(y); // true
(五)与 null 的比较
对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false
x.equals(null); // false;
2.equals() 与 ==
(一)== 双等号
- 基本数据类型:比较值是否相等
- 引用类型:比较地址是否同一个对象
int a=10; int b=10;
a==b; // true 值相等
String s1 = new String("abc");
String s2 = new String("abc");
s1==s2; // false 地址不一样
(二)equals()
- 默认继承 Object:底层就是 ==,比较地址
- String 重写了 equals:比较字符串内容是否一样
s1.equals(s2); // true 内容相同
(三)String 专属大坑
String s1 = "abc";
String s2 = "abc";
s1==s2; // true 常量池同一个地址
String s3 = new String("abc");
s1==s3; // false
s1.equals(s3); // true
(四)总结
- == 比地址(引用)、比数值
- equals 默认比地址,String 重写后比内容
- 字符串判断相等,永远用 equals,别用 ==
hashCode()
hashCode() 是 Java 根类 java.lang.Object 中的核心方法,所有 Java 对象都默认拥有这个方法,它的核心作用是返回对象的哈希码值,是 Java 集合(如 HashMap、HashSet)高效工作的基础。
核心作用
为哈希表(HashMap/HashSet/HashTable)提供快速定位能力:哈希表通过 hashCode() 计算对象的存储位置,避免逐个遍历对比,大幅提升查询 / 插入效率
hashCode () 的核心规范
- 一致性:同一个对象,在程序运行期间,多次调用
hashCode()必须返回相同的整数; - 等价性:如果两个对象通过
equals()判断为相等,那么它们的hashCode()必须相同; - 非强制性:如果两个对象
equals()不相等,不要求hashCode()不同(但建议不同,避免哈希冲突)。
默认实现 vs 重写实现
1. Object 类的默认实现
- 基于对象的内存地址计算哈希码;
- 即使两个对象内容完全一样,只要是不同实例(内存地址不同),
hashCode()一定不同。
2. 重写 hashCode ()(业务开发常用)
自定义对象时,必须和 equals() 一起重写,保证:内容相等 → hashCode 相等。
hashCode () 与 equals () 的关系
- equals 相等 → hashCode 一定相等(强制);
- hashCode 相等 → equals 不一定相等(哈希冲突);
- hashCode 不相等 → equals 一定不相等。
| 比较条件 | hashCode | equals |
|---|---|---|
| 对象 1 == 对象 2 | 相同 | 相同 |
| equals () 返回 true | 必须相同 | 相同 |
| equals () 返回 false | 建议不同 | 不同 |
| hashCode () 相同 | 相同 | 不一定 |
toString()
默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。
public class ToStringExample {
private int number;
public ToStringExample(int number) {
this.number = number;
}
}
ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());
ToStringExample@4554617c
clone()
clone() 是 Java 根类 Object 提供的一个原生方法,作用是创建并返回当前对象的一份副本(克隆),是 Java 中快速复制对象的标准方式。
一、核心基础
1. 方法定义
protected native Object clone() throws CloneNotSupportedException;
protected:只能在子类、同包类中调用,外部类直接调用会编译报错native:本地方法,底层由 C/C++ 实现,执行效率高- 抛出异常:克隆不支持的类会抛出
CloneNotSupportedException
2. 必须实现的接口:Cloneable
这是使用 clone() 的前提!Cloneable 是一个标记接口(没有任何方法),仅用于告诉 JVM:这个类允许被克隆。如果一个类没有实现 Cloneable,直接调用 clone() 一定会抛出异常。
二、浅克隆(默认)
特点
- 复制基本数据类型的值(完全独立)
- 仅复制引用数据类型的地址(不复制对象本身,原对象和克隆对象共享引用对象)
- 修改引用类型成员,会同时影响原对象和克隆对象
代码示例
// 1. 实现 Cloneable 标记接口
class User implements Cloneable {
// 基本类型
private int id;
// 引用类型
private String name;
// 2. 重写 clone() 方法,扩大访问权限为 public
@Override
public User clone() throws CloneNotSupportedException {
// 调用父类的克隆方法
return (User) super.clone();
}
// getter、setter、构造方法
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
// 测试
public class ShallowCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
User user1 = new User(1, "张三");
// 克隆对象
User user2 = user1.clone();
// 修改基本类型:互不影响
user2.setId(2);
System.out.println(user1.getId()); // 1
System.out.println(user2.getId()); // 2
// 验证:两个对象是不同实例(地址不同)
System.out.println(user1 == user2); // false
}
}
三、深克隆
特点
- 完全复制所有数据,包括基本类型和引用类型
- 原对象和克隆对象完全独立,修改任意成员都不会互相影响
- 实现方式:递归克隆引用类型成员
特点
- 完全复制所有数据,包括基本类型和引用类型
- 原对象和克隆对象完全独立,修改任意成员都不会互相影响
- 实现方式:递归克隆引用类型成员
代码示例
// 引用类型:也必须实现 Cloneable
class Address implements Cloneable {
private String city;
// 重写克隆方法
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
// getter、setter、构造
public Address(String city) { this.city = city; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
}
// 主类
class Person implements Cloneable {
private int age;
// 引用类型成员
private Address address;
// 深克隆核心:递归克隆引用类型
@Override
public Person clone() throws CloneNotSupportedException {
// 1. 先克隆自身
Person person = (Person) super.clone();
// 2. 手动克隆引用类型成员(关键步骤)
person.address = this.address.clone();
return person;
}
// getter、setter、构造
public Person(int age, Address address) {
this.age = age;
this.address = address;
}
public int getAge() { return age; }
public Address getAddress() { return address; }
}
// 测试
public class DeepCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("北京");
Person p1 = new Person(20, addr);
Person p2 = p1.clone();
// 修改克隆对象的引用类型成员
p2.getAddress().setCity("上海");
// 互不影响(深克隆成功)
System.out.println(p1.getAddress().getCity()); // 北京
System.out.println(p2.getAddress().getCity()); // 上海
}
}
四、总结
clone()是Object提供的对象复制方法,必须实现Cloneable才能使用- 默认是浅克隆,仅复制引用地址;手动改造可实现深克隆,完全独立
- 重写时要把权限改为
public,并处理CloneNotSupportedException异常
8.关键字
final
1. 数据
声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
- 对于基本类型,final 使数值不变;
- 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
final int x = 1;
// x = 2; // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;
2.方法
声明方法不能被子类重写。
private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。
3. 类
声明类不允许被继承。
static
1. 静态变量
-
静态变量: 又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它;静态变量在内存中只存在一份。
-
实例变量: 每创建一个实例就会产生一个实例变量,它与该实例同生共死。
public class A {
private int x; // 实例变量
private static int y; // 静态变量
public static void main(String[] args) {
// int x = A.x; // Non-static field 'x' cannot be referenced from a static context
A a = new A();
int x = a.x;
int y = A.y;
}
}
2. 静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。
public abstract class A {
public static void func1(){
}
// public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static'
}
只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。
public class A {
private static int x;
private int y;
public static void func1(){
int a = x;
// int b = y; // Non-static field 'y' cannot be referenced from a static context
// int b = this.y; // 'A.this' cannot be referenced from a static context
}
}
3. 静态语句块
静态语句块在类初始化时运行一次。
public class A {
static {
System.out.println("123");
}
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
}
}
4. 静态内部类
非静态内部类依赖于外部类的实例,而静态内部类不需要。
public class OuterClass {
class InnerClass {
}
static class StaticInnerClass {
}
public static void main(String[] args) {
// InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
StaticInnerClass staticInnerClass = new StaticInnerClass();
}
}
5. 静态导包
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
import static com.xxx.ClassName.*
6. 初始化顺序
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
public static String staticField = "静态变量";
static {
System.out.println("静态语句块");
}
public String field = "实例变量";
{
System.out.println("普通语句块");
}
最后才是构造函数的初始化。
public InitialOrderTest() {
System.out.println("构造函数");
}
存在继承的情况下,初始化顺序为:
- 父类(静态变量、静态语句块)
- 子类(静态变量、静态语句块)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)
更多推荐




所有评论(0)