Java传家宝:微信公众号(Java传家宝)、Java传家宝-B站、Java传家宝-知乎、Java传家宝-CSND
JAVA基础
本篇总结比较常见的JAVA基础,包括了面向对象三大特征、基本数据类型、泛型、字符串
面向对象三大特征
封装、继承、多态是面向对象的三大特征,这里总结一下他们的概念以及Java是如何实现的
封装
将数据和行为(方法)组合在一起成为一个对象。通过封装,对象的内部实现细节被隐藏,使用者不用关心这些细节,并且对象的内部状态也被保护起来,外部只能通过对象暴露的接口访问对象的内部属性。
![对象封装](https://img-blog.csdnimg.cn/img_convert/96829de1e0be816d314b390e5e6f96f9.png)
继承
在Java中,类可分为类,子类和超类。超类就是Object类,属于所有类的父类。子类是指类A通过extends继承类B,那么A就是B的子类,B就是A的父类。
Java中只存在单继承,即一个类继承一个类。
类A可以继承类B的私有域,但是不能直接使用。
final类不可继承
![Java继承](https://img-blog.csdnimg.cn/img_convert/fc4828a60a0631a7121b519c4cbdcb0a.png)
多态
《Java技术卷1》:一个对象变量可以指示多种实际类型的现象称为多态。多态的实现方式可以分为重载、重写。具体可以参见本人在虚拟机栈中对于方法调用的分派调用的说明,可以将重载的静态绑定,重写的动态绑定对应为静态分派和动态分派调用。
实际类型指在堆中实际产生的对象类型
静态类型指在程序中声明的变量类型
// Person 就是静态类型 Man就是实际类型
Person p = new Man();
基本数据类型
关于基本数据类型,直接做个表格比较直观
名称 | 大小(字节) | 默认值 | 包装类 | 常量池 |
---|---|---|---|---|
boolean | false | Boolean | ||
byte | 1 | (byte) 0 | Byte | [-128,127] |
char | 2 | '\u0000' | Character | [0,127] |
short | 2 | (short) 0 | Short | [-128,127] |
int | 4 | 0 | Integer | [-128,127] |
float | 4 | 0.0f | Float | |
long | 8 | 0L | Long | [-128,127] |
double | 8 | 0.0d | Double |
常量池是指大部分包装类会缓存一定数值范围内的数据,下次或者直接获取缓存数据即可。
关于包装类的作用:
-
在集合中,定义类型时只能使用包装类 -
泛型参数不能是基本类型,要使用只能使用其包装类
在说下自动拆装箱:
// 自动装箱 ==等价于==>> Integer i = Integer.ValueOf(1);
Integer i = 1;
// 自动拆箱 ==等价于==>> int j = i.intValue;
int j = i;
自动拆箱会引发NPE(null pointer error)问题:i.intValue,其中如果i == null时,就触发了NPE问题,常见于从数据库查询数据为空,然后赋值给基本类型出现。
泛型
Java在Jdk1.5之后加入了泛型,包括有泛型类,泛型接口,泛型方法。另外集合还通过泛型参数传入指定类型的对象,提高代码的可读性。但是,值得注意的是,Java的泛型是伪泛型,经过编译后,会经历泛型擦除,通过强转的方式保持原类型。
List<String> list = new ArrayList<>();
list.add("你好");
System.out.print(list.get(0));
//##########泛型擦除后###############
List list = new ArrayList();
list.add("你好");
System.out.print((String) list.get(0));
字符串
字符串可分为可变字符串String和不可变字符串StringBuilder和StringBuffer。
不可变字符串
首先String在jdk1.8是采用的char数组存储,jdk1.9变成了byte数组。并且它是不可变的。关于字符串只需讲一个案例就会比较通透了。
// 思考:产生了几个String对象?
String str = new String("Hello");
首先看图说话:
![构造器创建String对象](https://img-blog.csdnimg.cn/img_convert/2494c75a1cca7a6e9a2fb847ea6e8080.png)
可以看到,创建了2个String对象,其中一个用于将其置于字符串常量池中,另外一个用于方法调用。但是,注意以下几点:
-
如果常量池已经 存在相同的字符串引用,则只创建 一个String对象 -
如果用 字面量创建字符串,只 创建一个对象,且如果字符串 常量池本就存在,那么直接返回, 不创建String对象
可变字符串
分为StringBuilder线程不安全和StringBuffer通过Synchronized实现线程安全。这两个内部的实现都差不多,先看一下结构图:
![继承关系](https://img-blog.csdnimg.cn/img_convert/d1dc20c180cded1a93d975d6c1f03ebb.png)
就拿StringBuilder来解析吧,主要解析append过程和初始化构造器,先给结论:
-
初始化 无参数默认长度为 16, 有参数则默认长度为该 参数的长度+16 -
append如果长度不够,扩充为原来的 2倍+2,最大为 Integer.MAX_VALUE - 8 -
扩充过程通过 Arrays.copyOf(数组,新长度) -
最后添加过程通过 String.getChars(字符串添加的起始位置,字符串添加的最后位置,数组,开始添加的索引位置)
char[] chars = new char[16];
String str = "Hello";
chars[0] = 'n';
// srcBegin-0 srcEnd-str.length()-1 char[]-chars dstBegin-1
str.getChars(0,str.length()-1,chars,1);
for (char c :
chars) {
System.out.print(c);
}结果
![]()
image-20231113152410608
在跟进append源码:
int count; // 数组使用的索引数量
char[] value; // 存放字符的数组
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
// 1 判断容量是否足够
ensureCapacityInternal(count + len);
// 2 添加字符串到数组中
str.getChars(0, len, value, count);
count += len;
return this;
}
跟进第一步ensureCapacityInternal(count + len):
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
// 扩容
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private int newCapacity(int minCapacity) {
// overflow-conscious code
// 扩容为2倍+2
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
// 返回新容量:原容量的2倍+2 > MAX_ARRAY_SIZE ? MAX_ARRAY_SIZE : 原容量的2倍+2
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
抽象类和接口
抽象类和接口都是用于提炼其他类的公共部分,减少代码冗余。抽象类主要用于提炼所有同类型的共有特征,而接口的要求没这么高,因为它可以有多个实现。
抽象类的特点:
-
abstract修饰的所有类,包含抽象方法的类必须声明为抽象类
-
抽象类不能实例化
-
抽象类方法可以有默认的实现
接口的特点:
-
不能实例化,只能被其他类实现 -
可以有默认实现,不过需要添加 default关键字 -
方法默认都是 public
![接口和抽象类](https://img-blog.csdnimg.cn/img_convert/7e7390289814d79e71269a55780733c6.png)
异常
异常的顶层接口为Throwable,分支为Exception和Error。其中 Error ⽤来表示 JVM ⽆法处理的错误,Exception 分为两种:
-
受检异常 :需要⽤ try...catch...finally 语句捕获并进⾏处理,并且可以从异常中恢复; -
非受检异常:是程序运⾏时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且⽆法恢复
![异常体系](https://img-blog.csdnimg.cn/img_convert/df89823c7124c70eaa53aa855d9f44fb.png)