Java基础复习-JDK基础
Java基础复习-JDK基础
由于基础的东西很多、很杂,所以这里也只能是想到什么是什么
1.OOP(面向对象)
面向对象是一种重要的软件开发思想,将待解决的问题通过对象的设计进行解决。
OOP有四个重要的特性:
- 封装:类的信息被隐藏在类内部,一般不允许外部直接去访问类的数据,而是通过类的方法进行访问和操作。
- 继承:继承是通过已有的父类派生新的子类,子类可以继承父类本来的属性、方法和特性,并可以在其基础上进行扩展。Java类只能继承一个非final类。
- 多态:多态是指在不改变代码的情况下,改变运行指定的代码部分从而实现不同的功能。多态的三个重要要素是继承、重写、父类引用指向子类对象。重载方法可以在同一个类中根据不同的参数列表执行不同的方法主体,子类重写父类方法可以通过对象的实际类型来执行相应的方法。
- 抽象:将客观的事物通过代码抽象出来,抽象类、抽象方法。
面向对象编程有六个重要的原则:
- 对象单一职责:我们设计的对象,其负责的内容(属性、方法等)必须要专一,各个对象有明确的职责分工
- 里氏替换原则:子类应当能够完全代替父类的功能。这意味着子类必须完全实现父类的所有方法
- 开闭原则:对外扩展开放、对内修改关闭。也就是说尽量通过扩展而非修改的方式实现变更的需求。
- 迪米特法则:高内聚、低耦合,避免类与类之间在细节上的依赖。
- 依赖倒置原则:高层模块应该依赖于底层的抽象。实现类应该依赖于接口和抽象类。
- 接口隔离原则:要求接口设计大小适中。
2.反射
Java的反射机制是指:在代码运行的过程中,对于任何一个类、可以知道他的具体类、内部属性和方法,而对于任何一个对象可以去调用其内部的方法。
在IDE开发工具中,会通过反射的机制来解析对象的类,为开发者展示相应的属性、方法甚至注释等信息。
在AOP编程中,JDK动态代理会通过反射的方式去调用代理对象的方法。
JDBC连接数据库时也会通过反射机制去加载数据库的加载程序。
invoke关键词可以用于执行通过反射得到的方法等可执行代码
下面是一个典型的反射代码案例,包含了常见的反射操作:
// 获取 Person 类的 Class 对象
Class<?> personClass = Class.forName("Person");
// 获取并打印所有构造方法
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("Constructor: " + constructor);
}
// 创建 Person 对象实例
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
Object person = constructor.newInstance("John Doe", 30);
// 获取并打印所有方法,包括修饰方法的关键词
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println("Method: " + method);
}
// 调用 setName 方法
Method setNameMethod = personClass.getMethod("setName", String.class);
setNameMethod.invoke(person, "Jane Doe");
// 调用 getName 方法
Method getNameMethod = personClass.getMethod("getName");
String name = (String) getNameMethod.invoke(person);
System.out.println("Name: " + name);
// 获取并打印所有字段
Field[] fields = personClass.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field: " + field);
}
// 访问私有字段 name
Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person, "John Smith");
String updatedName = (String) nameField.get(person);
System.out.println("Updated Name: " + updatedName);
输出如下:
Constructor: public Person(java.lang.String,int)
Method: public java.lang.String Person.getName()
Method: public java.lang.String Person.toString()
Method: public void Person.setName(java.lang.String)
Method: public void Person.setAge(int)
Method: public int Person.getAge()
//下面的方法继承自Object类
Method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
Method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
Method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
Method: public boolean java.lang.Object.equals(java.lang.Object)
Method: public native int java.lang.Object.hashCode()
Method: public final native java.lang.Class java.lang.Object.getClass()
Method: public final native void java.lang.Object.notify()
Method: public final native void java.lang.Object.notifyAll()
Name: Jane Doe
Field: private java.lang.String Person.name
Field: private int Person.age
Updated Name: John Smith
3.序列化和反序列化
序列化是指将对象转换为特定的字节序列,反序列化则是将字节序列转换为对象数据。
在数据持久化的过程中,例如将数据存储到Redis中,会通过序列化和反序列化的技术实现对象的存储。例如使用Jackson将对象序列化为json数据。实现序列化和反序列化需要实现 Serializable 接口。
4.接口和抽象类的区别
- 一个类只能继承一个抽象类,但是可以实现多个接口
- 接口中不能够含有静态方法和静态代码块,而抽象类可以
- 抽象类中可以有实现的方法,而接口应该提供抽象方法
- 抽象类和类更接近,但是不能够被实例化。
5.final和static关键词
final和static都是用于修饰类、方法、属性的关键词
final的特点:
- final类是最终类,不能被继承。
- final方法不能够被子类重写
- final修饰的变量会作为常量,不能修改
static的特点:
- static类可以用于修饰内部类(不能修饰外部类)作为外部类的静态成员,只能访问外部类的静态方法
- static方法属于类,无需通过实例化的对象调用
- static变量属于静态变量,在最初就会被创建,该类实例化的所有对象共享static数据
- static还可以修饰代码块,静态代码块会在类最初随着静态属性一同实例化
6.浅拷贝和深拷贝
对象的复制和拷贝可以通过实现Cloneable接口并重写clone()方法实现,也有其他实现方法
浅拷贝:复制对象时只复制对象本身和对象的基本数据类型,如果存在对象引用,则不会变化
实现方式:
//拷贝构造方法
public Person(Person p){
this.age = p.age;
this.name = p.name;
}
//工厂方法
public Person clone(Person p){
return new Person(p.age,p.name);
}
//重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
深拷贝:复制对象除了复制对象本身和对象内部的基本数据类型之外,对引用的对象也会进行一次复制,这样拷贝对象和原始对象引用的对象不是同一个对象。
实现方式:
//1、重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Animal animal = (Animal)super.clone();
animal.setDog((Dog)animal.getDog().clone());
return animal;
}
//2、对象序列化与反序列化,需要实现Serializable接口
public static <T extends Serializable> T deepCopy(T object) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(outputStream);
output.writeObject(object);//对象序列化
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream input = new ObjectInputStream(inputStream);
return (T) input.readObject();//对象反序列化
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//3、手动递归复制,代码略
7.Java创建对象的4个方式
- 1.直接通过new调用构造方法创建对象
- 2.通过反射机制,调用类的Class.newInstance()方法创建对象
- 3.通过clone()方法对已有对象进行拷贝复制
- 4.反序列化将字节序列转换为对象
8.泛型
允许类、接口和方法接受多种类型的参数进行定义
下面是泛型类的实际代码演示
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
以下是泛型方法的实际代码演示
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
通配符"?",可以通过"?"表示未知类型
使用extends和super关键词可以对泛型的类型进行限制,如下所示
class MyClass<T extends Number>{}
class MyClass<T super Number>{}
9.基本数据类型
Java中共有8个基本数据类型,详细信息见下表
| 数据类型 | 储存内容 | 占用字节 |
|---|---|---|
| byte | 字节 | 8bit | 1字节 |
| char | 字符 | 16bit | 2字节 |
| short | 短整型 | 16bit | 2字节 |
| int | 整型 | 32bit | 4字节 |
| float | 浮点数 | 32bit | 4字节 |
| long | 长整型 | 64bit | 8字节 |
| double | 双精度浮点数 | 64bit | 8字节 |
| boolean | 布尔值 | 1bit | 1字节或4字节 |
10.装箱和拆箱
装箱(Boxing)是将基本数据类型(int,double等)值转换为相应包装器类(Integer,Double等)的实例对象。装箱可以实现利用对象的特性(引用传递、储存在集合类中、传递对象参数等)
JDK5开始编译器会自动装箱,比如可以直接用int向Integer传值
int a = 10;
Integer b = a;//自动装箱,JDK5以后。隐性调用了Integer.valueOf(a)
Integer c = new Integer(a);//手动装箱,JDK5以前
拆箱(Unboxing)是将包装器对象转换为基本数据类型值,通常应用在算术运算、比较、赋值基本类型变量等场合
JDK5开始也提供自动拆箱的功能,实现对象为基本数据类型传值
Integer a = 10;
int b = a;//自动拆箱,JDK5以后。隐性调用了xxxValue()方法
int c = a.intValue();//手动拆箱,JDK5以前
要注意对象之间的比较应当用equals(),而不是用==,但下面的情况除外
常量池优化: 对于Integer、Byte、Short、Long、Character对象,-128至127之间的数值会重复使用已创建的对象,采用缓存策略,即创建对象时为相同引用 因此这种情况可以使用==进行比较,如下
Integer a = 125;
Integer b1 = 125;
Integer b2 = 124 + new Integer(1);
System.out.println(a == b1);//true,实质上是同一个对象
System.out.println(a == b2);//true,计算不影响规则
Integer c = 200;
Integer d = 200;
System.out.println(c == d);//false,超出常量池范围,实质是两个对象
11.注解
注解(Annotation)是JDK5引入的一种代码级别的说明,目前绝大多数的Java框架都是基于注解+反射实现的
(1)注释的功能:
- 编译检查:例如
@Override检查重载重写情况 - 代码分析:利用反射机制对代码进行分析处理
- 动态处理:Java框架在编译时读取注解信息
- 生成文档:Javadoc生成API文档
(2)自定义注解的语法: 与接口相似,但这里interface前加了@ 注解支持的参数类型包括八大基础数据类型、String、Class、Enum和其余注解,及上述类型的数组
//定义
public @interface MyAnnotation {
String value() default ""; //定义value属性
}
//使用
@MyAnnotation(value="This is my custom annotation")
public class MyClass {
}
(3)元注解: 用于标识注解信息的注解
| 注解名 | 功能 | 参数 |
|---|---|---|
| @Retention | 确定注解生命周期,注解可用范围 | RetentionPolicy |
| @Target | 指定注解可应用源码元素 | ElementType |
| @Inherited | 允许子类继承父类注解 | - |
| @Documented | 注解信息会被写入到Javadoc | - |
| @Repeatable | 同一注解可在同一声明前重复使用 | RepeatedValues |
(4)JDK内置注解: 由编译器使用的注解,不影响代码功能逻辑
| 注解名 | 功能 | 适用范围 |
|---|---|---|
| @Override | 指定重写方法,编译器检查报错 | 方法 |
| @Deprecated | 标记过时元素,编译器警告 | 类、方法等 |
| @SuppressWarnings | 忽略编译器警告 | 参数deprecation、unchecked、fallthrough、path、serial、finally、all |
| @SafeVarargs | 抑制堆污染警告 | 方法、函数 |
| @FunctionalInterface | 标记接口为函数式接口(只有一个抽象方法) | 接口 |
(5)注解提取与处理: 注解的提取和处理都需要借助Java强大的反射机制来实现,反射机制提供了一系列注解相关的方法
| 方法 | 描述 |
|---|---|
| isAnnotationPresent() | 判断是否应用某注解 |
| getAnnotations() | 返回所有注解 |
| getDeclaredAnnotations() | 返回直接作用于对象的所有注解 |
| getAnnotation() | 返回指定类型注解 |
| getDeclaredAnnotation() | 返回直接作用于对象的指定类型注解 |
| getAnnotationsByType() | 返回指定类型的所有注解 |
| getDeclaredAnnotationsByType() | 返回直接作用于对象的指定类型的所有注解 |