反射Reflect
类对象,把类当成对象来看
获取类的类对象
1.通过对象获取 对象.getClasss()
Student student = new Student("chichi", 18, "shen");
Class<? extends Student> aClass = student.getClass();
2.类名.class
Class<Student> studentClass = Student.class;
3.静态方法 Class.forname(“类的全名称”); 推荐 耦合性低,灵活,传入字符串,编译时即使没有该类也不会报错
Class<?> aClass1 = Class.forName("week4.day17.Student");
类对象在内存中只有一个
获取类的一些属性
public static void main(String[] args) throws Exception{
Class<?> class1 = Class.forName("week4.day17.Student");
System.out.println(class1.getName());
System.out.println(class1.getSimpleName());
System.out.println(class1.getPackage().getName());
// 获得父类
Class<?> superclass = class1.getSuperclass();
// 获得接口
Class<?>[] interfaces = class1.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface.getName());
}
获取构造方法类,创造实例对象
// 获取构造方法
// 获得全部构造方法
Constructor<?>[] constructors = class1.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
// 获得单个构造
// 1.有参构造
Constructor<?> constructor1 = class1.getConstructor(String.class, int.class, String.class);
Student s1 =(Student)constructor1.newInstance("chichi", 18, "nan");
// 2.无参构造
Constructor<?> constructor2 = class1.getConstructor();
Student s2= (Student) constructor2.newInstance();
// 使用无参简单创建对象的简单方式
Student s3 = (Student) class1.newInstance();
获取方法
Class<?> class1 = Class.forName("week4.day17.Student");
// 获取全部方法
// getMethods()获取全部的公开方法,包括继承来的公开方法,没有私有默认,保护方法
// getDeclaredMethods 获取类中全部方法,不包括继承来的
Method[] methods = class1.getMethods();
Method[] declaredMethods = class1.getDeclaredMethods();
//获取单个方法
Method show = class1.getMethod("show");
// 1.调用无参方法
Student s1 = (Student) class1.newInstance();
show.invoke(s1);
// 2.调用有参方法
Method show1 = class1.getMethod("show", String.class);
show1.invoke(s1,"chichi");
// 3. 调用有返回值的方法
Constructor<?> constructor = class1.getConstructor(String.class, int.class, String.class);
Student chichi = (Student) constructor.newInstance("chichi", 18, "99");
Method getName = class1.getMethod("getName");
String name = (String) getName.invoke(chichi);
// 4.调用私有方法
Method privateMethod = class1.getDeclaredMethod("privateMethod");
// 设置访问权限失效
privateMethod.setAccessible(true);
privateMethod.invoke(s1);
// 5.调用静态方法
Method staticMethod = class1.getMethod("staticMethod");
staticMethod.invoke(null );
有返回值的
获取属性
Class<?> class1 = Class.forName("week4.day17.Student");
// 获取全部字段
Field[] declaredFields = class1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
// 获取单个字段
Field name = class1.getDeclaredField("name");
// 设置访问权限
name.setAccessible(true);
Student s1= (Student) class1.newInstance();
// 设置字段
name.set(s1,"chichi");
// 获取字段
System.out.println(name.get(s1));
反射的优缺点
内省
属性:如果类中包含了getXxx,setXxx,isXxx开头的方法,表示类中有xxx属性,这个属性并不指字段
属性操作方式:
1.反射属性(暴力破解,需设置字段访问权限)
2.反射setXxx方法(麻烦,还需提前了解属性的名称及类型)
3.内省 不需要事先知道属性的类型,属性的名称
// 不需要事先知道属性的名称和他的类型
public static void main(String[] args) throws Exception{
Class<?> class1 = Class.forName("week4.day17.Student");
Student student = (Student) class1.newInstance();
// 获得属性名称,以便创建描述符
BeanInfo beanInfo = Introspector.getBeanInfo(class1);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
// for (PropertyDescriptor pd : propertyDescriptors) {
// System.out.println(pd.getName());
// }
// 获得描述符
// 1.根据遍历结果通过下标获得
// PropertyDescriptor pdAge = propertyDescriptors[0];
// 2.根据遍历结果新建描述符对象
PropertyDescriptor pdAge=new PropertyDescriptor("age",class1);
//获得属性类型,以便赋值
System.out.println(pdAge.getPropertyType());
// 赋值
Method writeMethod = pdAge.getWriteMethod();
writeMethod.invoke(student,18);//实际调用setAge方法
//获得值
Method readMethod = pdAge.getReadMethod();
System.out.println(readMethod.invoke(student));//实际调用getAge方法
}
设计模式
简单工厂模式
使用配置文件,反射进而达到开闭原则,配置文件使用properties文件
单例模式
1.饿汉式:类一加载,对象就初始化
缺点:生命周期长,浪费空间 优点:线程安全
懒汉式:
public class SingleCon {
// 懒汉模式
// 1.私有化构造方法
private static boolean f = false;
private SingleCon(){
if (f){
// 防止使用反射强行破解单例模式(当然不能完全防止)
// 当被反射会调用构造方法 抛出运行时异常
throw new RuntimeException("不要使用反射破解");
}
}
// 在类内部创建一对象
private static volatile SingleCon instance;//防止指令重排
// 公开方法返回对象
public static SingleCon getInstance(){
//有线程创建了对象了,其他线程就不要在阻塞等着锁再来进行判断了,直接返回
if (instance==null) {//目的:提高效率,双重检查
synchronized (SingleCon.class){//防止多个线程判断为空时创建,造成多个对象
if (instance==null){
//创建实例
instance=new SingleCon();
f=true;
}
}
}
return instance;
}
}
外层判断避免多个线程在变量已经实例化后还需要拿锁,当锁住并创建后,其他线程就不在需要拿锁了
volatile 禁止指令重排
好处:声明周期短,节省空间 缺点:线程不安全,必须使用同步解决
3.静态内部类写法
最好的写法
// 使用静态内部类
// 1.私有化构造方法
private SingleStatic(){}
// 2.在静态类内部
static class Inner{
private static SingleStatic instance = new SingleStatic();
}
// 3.通过公开方法返回这个对象
public static SingleStatic getInstance(){
return Inner.instance;
}
优点: 1.安全 2.生命周期短,节省空间
枚举
锁定了取值范围的数据类型
枚举其实就是静态常量
注意问题
1.枚举举常量直接用逗号隔开,最后分号可写可不写,如果后边有代码,必须加分号
2. 枚举只能包含私有构造方法,属性和普通方法,必须写在常量后边
public enum Gender {
MAN,WOMEN;//常量写在最前边
private Gender(){}
int s=8;
public void show(){
}
}
注解
反编译:本质上就是接口,其属性就是公开抽象方法
属性的类型只能是:1.基本类型 2.String 3.Class类型 4.枚举类型 5.注解类型
以及上述类型的一维数组
只能包含公开属性,定义属性时,可以给默认值,没给默认值得使用时必须要赋值
如果注解中使用value属性名,当只要value一个属性需要赋值时可以不用写属性名,直接赋值
元注解
给注解的注解,放在自定义注解的上边
使用反射获取注解信息
//运行程序时,JVM会保留,这样才能通过反射获取到注释
// 否则获取不到注解
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfo {
String name() default "chichi";
int age() default 18;
String gender();
String value();
}
public class Person {
private String name;
private int age;
private String gender;
public Person() {
}
// 特殊 只有value一个属性需要赋值时,可不写属性名
@PersonInfo(gender = "男", value = "value")
public void printInfo(){
try {
// 获取类属性
Class<?> class1 = Class.forName("week4.day17.annotation.Person");
// 获取方法
Method printInfo = class1.getMethod("printInfo");
// 获取注解
PersonInfo annotation = printInfo.getAnnotation(PersonInfo.class);
// 获取注解属性
System.out.println(annotation.name());
System.out.println(annotation.age());
System.out.println(annotation.gender());
System.out.println(annotation.value());
} catch (Exception e) {
e.printStackTrace();
}
}
}
注解应用
// 让反射读取到
@Retention(RetentionPolicy.RUNTIME)
// 应用到方法上
@Target(ElementType.METHOD)
public @interface ContentType {
String value();
}
// 判断方法是否被注解修饰
if (method.isAnnotationPresent(ContentType.class)){
ContentType annotation = method.getAnnotation(ContentType.class);
String value = annotation.value();
}