目录
前言
最近看面试题的时候,看到有关反射的面试,由于上课学的时候老师压根没讲反射的内容,所以今天又来补漏洞啦
一、反射是什么
反射到底是个啥?《Java核心技术》书中给出的解释是:能够分析类能力的程序称为反射。反射机制可以用来:
- 在运行时分析类的能力
- 在运行时查看对象,例如,编写一个toString
- 实现通用的数组操作代码
- 利用Method对象,这个对象很像C++中的函数指针
通俗来说,反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
因此,反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。
二、类对象
- 类的对象:基于某个类new出来的对象,也称为实例对象。
- 类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
也就是说,每个类加载到内存都对应一个Class对象,每个类有且只有一个Class对象
显示当前程序所加载的类:JVM参数-verbose:class
三、获取类对象
3.1 通过类的对象,获取类对象
/**
* @author: Radish
* @date: 2020-10-07 19:10
*/
public class TestPerson {
public static void main(String[] args) {
Person person = new Person("张三");
Class<?> aClass = person.getClass();
System.out.println(aClass);
}
}
3.2 通过类名获取类对象
/**
* @author: Radish
* @date: 2020-10-07 19:10
*/
public class TestPerson {
public static void main(String[] args) {
System.out.println(Person.class);
}
}
3.3 通过静态方法获取类对象(推荐)
/**
* @author: Radish
* @date: 2020-10-07 19:10
*/
public class TestPerson {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.reflect.Person");
}
}
若类名不存在,则会抛出异常
3.4 常用方法
使用反射获取类的构造方法,创建对象
//使用反射获取类的构造方法,创建对象
public static void reflectOpe2() throws Exception {
//(1)获取类对象
Class<?> aClass = Class.forName("com.reflect.Person");
//(2)获取类的构造方法Constructor
System.out.println("===========获取类的构造方法============");
Constructor<?>[] cons = aClass.getConstructors();
for (Constructor<?> con : cons) {
System.out.println(con);
}
//(3)获取类中的无参构造
System.out.println("===========获取类的无参构造============");
Constructor<?> con = aClass.getConstructor();
System.out.println(aClass);
Person lisi = (Person) con.newInstance();
System.out.println(lisi);
//(4)获取类中带参构造方法
System.out.println("===========获取类的带参构造============");
Constructor<?> con1 = aClass.getConstructor(String.class, int.class);
Person p2 = (Person) con1.newInstance("李四", 23);
System.out.println(p2);
}
使用反射获取类中的方法,并调用方法
//3使用反射获取类中的方法,并调用方法
public static void reflectOpe3() throws Exception {
//(1)获取类对象
Class<?> aClass = Class.forName("com.reflect.Person");
//(2)获取方法Method对象
System.out.println("========获取方法Method对象========");
//Method[] methods = aClass.getMethods(); 获取公开的方法,包括从父类继承的方法
Method[] methods = aClass.getDeclaredMethods(); //获取类中的所有方法,包括私有、默认、保护的、不包含继承的方法
for (Method method : methods) {
System.out.println(method);
}
//(3)获取单个方法
System.out.println("===========获取单个方法===========");
System.out.println("----------不带参----------");
Method eatMethod = aClass.getMethod("eat");
//调用方法
//正常调用方法 Person person = new Person(); person.eat;
Person zhangsan = (Person) aClass.newInstance();
eatMethod.invoke(zhangsan);
//toString
Method toStringMethod = aClass.getMethod("toString");
Object result = toStringMethod.invoke(zhangsan);
System.out.println(result);
System.out.println("----------带参----------");
Method eatMethod2 = aClass.getMethod("eat", String.class);
eatMethod2.invoke(zhangsan, "鸡腿");
//(4)获取私有方法
System.out.println("===========获取私有方法===========");
Method privateMethod = aClass.getDeclaredMethod("privateMethod");
//设置访问权限无效
privateMethod.setAccessible(true);
privateMethod.invoke(zhangsan);
//(5)获取静态方法
System.out.println("===========获取静态方法===========");
Method staticMethod = aClass.getMethod("staticMethod");
staticMethod.invoke(null);
}
这里需要注意几点:①getMethods方法是获取公开的方法,包括从父类继承的方法;getDeclaredMethods方法是获取类中的所有方法,包括私有、默认、保护的、不包含继承的方法。②在获取私有方法时,需设置访问权限无效Method setAccessible(true)
使用反射实现一个可以调用任何对象方法的通用方法
//4使用反射实现一个可以调用任何对象方法的通用方法
public static Object invokeAny(Object obj, String methodName, Class<?>[] types,Object...args) throws Exception {
//1获取类对象
Class<?> aClass = obj.getClass();
//2获取方法
Method method = aClass.getMethod(methodName, types);
//3调用
return method.invoke(obj,args);
}
public static void main(String[] args) throws Exception {
//reflectOpe2();
//reflectOpe3();
Properties properties = new Properties();
invokeAny(properties, "setProperty", new Class[]{String.class,String.class}, "username", "张三");
System.out.println(properties);
}
使用反射获取类的属性
//5使用反射获取类的属性
public static void reflectOpe4() throws Exception{
//(1)获取类对象
Class<?> aClass = Class.forName("com.reflect.Person");
//(2)获取属性(字段) 公开的字段,父类继承的字段
// Field[] fields = aClass.getFields();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//(3)获取name属性
Field name = aClass.getDeclaredField("name");
name.setAccessible(true);
System.out.println(name);
//(4)赋值
Person zhangsan = (Person) aClass.newInstance();
name.set(zhangsan, "张三");
//(5)获取值
System.out.println(name.get(zhangsan));
}
四、设计模式
4.1 什么是设计模式
一套被反复调用、多数人知晓的、经过分类编目的、代码设计经验的总结。
- 好处:使用设计模式为了可重用代码、让代码更容易被他人理、保证代码可靠性、重用性。
4.2 工厂设计模式
- 工厂设计模式主要负责对象创建的问题
- 开发中有一个非常重要的原则“开闭原则”,对扩展开放、对修改关闭。
- 可通过反射进行工厂模式的设计,完成动态的对象创建。
添加实现类时只需修改配置文件
4.3 单例模式
- 单例:只允许创建一个该类的对象。
方式1:饿汉式(类加载时创建,天生线程安全)
class Singleton {
private static final Singleton = new Singleton();
private Singleton();
public static Singleton getInstance(){
return instance;
};
}
方式2:懒汉式(使用时创建,线程不安全,加同步,效率低)
class Singleton {
private static final Singleton = null;
private Singleton();
public static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
};
}
方式3:懒汉式(使用时创建,线程安全)
class Singleton {
private Singleton();
private static class Holder() {
static Singleton s = new Singleton();
}
public static Singleton instance(){
return Holder.s;
};
}
五、枚举
5.1 什么是枚举
枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。
- 枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性。
- 定义枚举使用enum关键字。
- 枚举的本质:
- 枚举是一个终止类,并集成enum抽象类
- 枚举中常量是当前类型的静态常量
六、注解
6.1 什么是注解
注解是代码里的特殊标记,程序可以读取注解,一般用于替代配置文件。
-
开发人员可以通过注解告诉类如何运行。
- 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
-
常见注解:@Override、@Deprecated
-
定义注解使用@interface关键字,注解中只能包含属性
定义一个注解:
使用
6.2 注解属性类型
- String类型
- 基本数据类型
- Class类型
- 枚举类型
- 注解类型
- 以上类型的一堆数组
6.3 元注解
-
元注解:用来描述注解的注解
-
@Retention:用于指定注解可以保留的域。
- RetentionPolicy.CLASS:注解记录在class文件中,运行java程序时,JVM不会保留
- RetentionPolicy.RUNTIME:注解记录在class文件中,运行java程序时,JVM会保留,程序可以通过反射获取该注释
- RetentionPolicy.SOURCE:编译时直接丢弃这种策略的注释。
-
@Target:指定注解用于修饰类的哪个成员