反射
反射是Java中的一种机制,可以通过Java代码对一个类进行解析;例如获取类的属性、方法、构造方法等。
获取Class对象相关方法
public T newInstance()
:创建此 Class 对象所表示的类的一个新实例。要求:类必须有public的无参数构造方法
示例
Student类
package com.qfedu.demo;
/**
* @Author pengyu
* @Date 2022/8/12 18:34
*/
public class Student {
private String name;
public int id;
//如果在这里加上transient便不会进行序列化,我就不演示了
//transient char sex;
char sex;
public Student() {
}
public Student(String name, int id, char sex) {
this.name = name;
this.id = id;
this.sex = sex;
}
private Student(String name) {
this.name = name;
}
private Student(int id) {
this.id = id;
}
public void say () {
System.out.println("我会说话!!!");
}
public void print(String name) {
System.out.println(name + "写作业!");
}
private void sleep () {
System.out.println("小宝宝,该睡觉了哦!");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
", sex=" + sex +
'}';
}
}
测试
package com.qfedu.demo;
import java.lang.reflect.Constructor;
/**
* @Author pengyu
* @Date 2022/8/12 18:51
*/
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException {
// 三种获得Student的Class的对象
Class<Student> studentClass = Student.class;
System.out.println(studentClass);
//("com.qfedu.demo.Student")全限定性类名
Class<?> aClass = Class.forName("com.qfedu.demo.Student");
System.out.println(aClass);
Student student = new Student();
Class<? extends Student> aClass1 = student.getClass();
System.out.println(aClass1);
/**
* 控制台打印结果:
* class com.qfedu.demo.Student
* class com.qfedu.demo.Student
* class com.qfedu.demo.Student
*/
}
}
Class中获取Constructor(构造方法)对象相关方法
相关方法
public Constructor getConstructor(Class... parameterTypes)
:根据参数类型获取构造方法对象,只能获得public修饰的构造方法。如果不存在对应的构造方法,则会抛出java.lang.NoSuchMethodException 异常。Constructor getDeclaredConstructor(Class... parameterTypes)
:根据参数类型获取构造方法对象**,能获取所有的构造方法(public、默认、protected、private )**。如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。Constructor[] getConstructors()
: 获取所有的public修饰的构造方法Constructor[] getDeclaredConstructors()
:获取所有构造方法,包括public、默认、protected、privateT newInstance(Object... initargs)
: 根据指定参数创建对象。void setAccessible(true)
:开启强制访问,除public修饰的构造方法外,其他构造方法反射都需要暴力反射
示例
package com.qfedu.demo;
import java.lang.reflect.Constructor;
/**
* @Author pengyu
* @Date 2022/8/12 18:58
*/
public class Demo03 {
public static void main(String[] args) throws NoSuchMethodException {
//获取Class对象
Class<Student> studentClass = Student.class;
//获取public修饰的所有构造方法,在一个数组中
Constructor<?>[] constructors = studentClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
/**
* 控制台打印结果:
* public com.qfedu.demo.Student(java.lang.String,int,char)
* public com.qfedu.demo.Student()
*/
//获取Student类中的所有构造方法
Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
/**
* 控制台打印结果:
* private com.qfedu.demo.Student(int)
* private com.qfedu.demo.Student(java.lang.String)
* public com.qfedu.demo.Student(java.lang.String,int,char)
* public com.qfedu.demo.Student()
*/
//获取public修饰的无参构造方法,null即为无参
Constructor<Student> constructor = studentClass.getConstructor(null);
System.out.println(constructor);
/**
* 控制台打印结果:
* public com.qfedu.demo.Student()
*/
//获取指定的构造方法,也就是说我们要获取有参的构造方法,只需在()里面添加相应的Class,多个用','隔开
Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
/**
* 控制台打印结果:
* private com.qfedu.demo.Student(int)
*/
}
}
Class中获取类中的Method(方法)相关方法
这个跟构造方法的意思一样
相关方法
public Method getMethod(String name, Class<?>... parameterTypes)
:根据方法名和参数类型获得一个方法对象,只能是获取public修饰的public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:根据方法名和参数类型获得一个方法对象,包含任意修饰符的public Method[] getMethods()
:获取所有的public修饰的成员方法,包括父类中的方法。public Method[] getDeclaredMethods()
:获取当前类中所有的方法,包含任意修饰符的,但不包括父类中。
常用方法
public Object invoke(Object obj, Object... args)
:根据参数args调用对象obj的该成员方法,如果obj=null,则表示该方法是静态方法public void setAccessible(boolean flag)
:开启强制访问,设置为可以直接调用非public修饰的方法public String getName()
:获取此对象封装的方法名
示例
package com.qfedu.demo;
import java.lang.reflect.Method;
/**
* @Author pengyu
* @Date 2022/8/12 19:15
*/
public class Demo04 {
public static void main(String[] args) throws NoSuchMethodException {
Class<Student> studentClass = Student.class;
//获取所有public修饰的方法,包括父类中的
Method[] methods = studentClass.getMethods();
// for (Method method : methods) {
// System.out.println(method);
// }
//获取Student类中的所有方法
Method[] declaredMethods = studentClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
/**
* 控制台打印结果:
* public void com.qfedu.demo.Student.say()
* public java.lang.String com.qfedu.demo.Student.toString()
* private void com.qfedu.demo.Student.sleep()
* public void com.qfedu.demo.Student.print(java.lang.String)
*/
//获取指定的方法,public修饰的,无参
Method say = studentClass.getMethod("say");
System.out.println(say);
/**
* 控制台打印结果:
* public void com.qfedu.demo.Student.say()
*/
//获取指定的方法,public修饰的,有参,可以传String类型的参数
Method print = studentClass.getMethod("print", String.class);
System.out.println(print);
/**
* 控制台打印结果:
* public void com.qfedu.demo.Student.print(java.lang.String)
*/
//获取指定的方法,可以是非public修饰的
Method sleep = studentClass.getDeclaredMethod("sleep");
System.out.println(sleep);
/**
* 控制台打印结果:
* private void com.qfedu.demo.Student.sleep()
*/
/**
* 方法的调用
* 1.要有对象
* 2.自己执行方法
*/
Student student = studentClass.newInstance();
print.invoke(student, "依依");
//控制台打印结果:依依写作业!
}
}
Class中获取Field(属性)相关方法
相关方法
Field getDeclaredField(String name)
:根据属性名获得属性对象,包括private修饰的Field getField(String name)
:根据属性名获得属性对象,只能获取public修饰的Field[] getFields()
:获取所有的public修饰的属性对象,返回数组。Field[] getDeclaredFields()
:获取所有的属性对象,包括private修饰的,返回数组。
常用方法
void set(Object obj, Object value)
:给指定的属性设置值Object get(Object obj)
:获取属性字段的值void setAccessible(boolean flag)
:开启强制访问Class getType()
:获取属性的类型,返回Class对象。
示例
package com.qfedu.demo;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MatchGenerator;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* @Author pengyu
* @Date 2022/8/12 18:35
*/
public class Test01 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//反射第一步:使用类来创建Class对象
Class<Student> studentClass = Student.class;
//第二步:获取无参构造方法
Constructor<Student> constructor = studentClass.getConstructor(null);
//第三步:通过无参构造创建对象
Student student = constructor.newInstance();
//我们如何赋值呢?
Field name = studentClass.getDeclaredField("name");
Field id = studentClass.getField("id");
Field sex = studentClass.getDeclaredField("sex");
//暴力反射,开启强制访问,因为name属性是private所修饰的,只要不是public修饰都需要暴力
name.setAccessible(true);
name.set(student, "卡哇伊");
id.set(student, 001);
sex.setAccessible(true);
sex.set(student, '女');
System.out.println(student);
//控制台输出打印结果:Student{name='卡哇伊', id=1, sex=女}
// 获取属性字段的值
Object o = name.get(student);
System.out.println(o);
//控制台输出打印结果:卡哇伊
}
}
序列化和反序列化
序列化:指把Java对象转换为字节序列的过程。
反序列化:指把字节序列恢复为Java对象的过程。
要求
- 只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
- 使用对应的API
- java.io.ObjectInputStream:对象输入流。 该类的readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。
- java.io.ObjectOutputStream:对象输出流。该类的writeObject(Object obj)方法将将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。
示例:序列化
package com.qfedu.demo;
import com.sun.xml.internal.ws.api.pipe.ServerTubeAssemblerContext;
import java.io.*;
/**
* @Author pengyu
* @Date 2022/8/12 19:35
*/
public class Demo07 {
public static void main(String[] args) throws IOException {
//序列化,这时我电脑路径中是没有这个文件的,它会自动生成
Student student = new Student("小哈", 001, '男');
FileOutputStream fos = new FileOutputStream(new File("D:/aaa/888.ser"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(student);
oos.close();
fos.close();
}
}
示例:反序列化
package com.qfedu.demo;
import java.io.*;
/**
* @Author pengyu
* @Date 2022/8/12 19:46
*/
public class Demo06 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//反序列化
FileInputStream fis = new FileInputStream(new File("D:/aaa/888.ser"));
ObjectInputStream ois = new ObjectInputStream(fis);
//强转
Student o = (Student)ois.readObject();
System.out.println(o);
//打印:Student{name='小哈', id=1, sex=男}
}
}
单例模式
单例模式有以下特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
懒汉式单例
package com.qfedu.demo;
/**
* @Author pengyu
* @Date 2022/8/12 19:54
*/
class A {
private static A a = null;
private A() {
}
public static A getA() {
if (a == null) {
a = new A();
}
return a;
}
}
public class Demo05 {
public static void main(String[] args) {
A a = A.getA();
A a1 = A.getA();
System.out.println(a); //com.qfedu.demo.A@154617c
System.out.println(a1); //com.qfedu.demo.A@154617c
System.out.println(a == a1); //true
}
}
饿汉式单例
class B {
private B() {}
private static final B b = new B();
public static B getInstance() {
return b;
}
}
1、线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。
懒汉式本身是非线程安全的。
2、资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
-
效率来说: 饿汉式效率高,因为不用加锁
-
性能来说: 饿汉式类一加载就实力出来对象。但是懒汉式调用方法的时候才进行创建对象,懒汉式的内存消耗是小于饿汉式的
JDK1.8新特性
- Lambda表达式
- 函数式接口
- *方法引用和构造器调用
- Stream API
- **接口中的默认方法和静态方法 **:可以看我之前726笔记,接口知识点
- 新时间日期API
Lambda表达式
Lambda主要是简化了匿名内部类的写法,并且Lambda表达式只能简化函数式接口的写法;也就是说,要使用Lambda表达式必须保证接口中只有一个抽象方法;
初体验
接口
package com.qfedu.lambda;
/**
* @Author pengyu
* @Date 2022/8/12 20:39
*/
public interface Task {
void run();
}
实体类
package com.qfedu.lambda;
/**
* @Author pengyu
* @Date 2022/8/12 20:39
*/
public class Person {
private Task task;
public void run() {
if (task != null) {
task.run();
}
}
public Person() {
}
public Person(Task task) {
this.task = task;
}
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
}
测试
package com.qfedu.lambda;
/**
* @Author pengyu
* @Date 2022/8/12 20:41
*/
public class Demo01 {
public static void main(String[] args) {
Person person = new Person();
person.setTask(new Task() {
@Override
public void run() {
System.out.println("跑起来了1!!!");
}
});
person.run();
Person person1 = new Person();
person1.setTask(
() -> {
System.out.println("跑起来了2!!!");
}
);
person1.run();
}
}
Lambda表达式的语法:
() -> {
System.out.println(“跑起来了2!!!”);
}
- 前面的一对小括号代表要实现的那个方法(Task接口的run方法)的参数列表。
- 中间的箭头是固定语法,用于分隔参数列表和方法体。
- 大括号里面就是实现的具体业务逻辑代码;当方法体中只有一句代码时,大括号可以省略。
接口 接口对象 = ()->表达式; 无参 无返回值的
接口 接口对象 = (parameter)->表达式; 有参 无返回值的
接口 接口对象 = ()->{表达式; return 返回值}; 无参 有返回值的
接口 接口对象 = (parameter)->{表达式; return 返回值}; 有参有返回值