反射(reflect)
把类当成对象来看
反射的相关类
获取类的类对象(三种方式)
//1通过对象获取类对象
Student student =new Student("学子",18,"男");
Class<? extends Student> aClass = student.getClass();
System.out.println("类对象的hashCode:"+aClass.hashCode());
//2通过类获取类对象,类.class
Class<Student> studentClass = Student.class;
System.out.println("类对象的hashCode:"+studentClass.hashCode());
//3通过Class方法,Class.forName(包名.类名)推荐使用这个,耦合性低,灵活
Class<?> aClass1 = Class.forName("qf.test.test01.Student");
System.out.println("类对象的hashCode:"+aClass1.hashCode());
获取类对象的信息
//创建类对象
Class<?> class1 = Class.forName("qf.test.test01.Student");
//得到带有包名的类名
System.out.println(class1.getName());
//得到类名
System.out.println(class1.getSimpleName());
//得到包名
System.out.println(class1.getPackage());
System.out.println(class1.getPackage().getName());
//遍历接口
Class<?>[] interfaces = class1.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface.getName());
}
//得到父类
Class<?> superclass = class1.getSuperclass();
System.out.println(superclass.getName());
获取构造类,创建实例对象
//遍历构造方法
Constructor<?>[] constructors = class1.getConstructors();
for (Constructor<?> constructor : constructors) {
//含有参数
System.out.println(constructor);
//只有包名 构造方法名 没有参数
System.out.println(constructor.getName());
}
//获取单个有参构造方法并实例化对象
Constructor<?> constructor = class1.getConstructor(String.class, int.class, String.class);
Student student = (Student)constructor.newInstance("haha", 12, "男");
System.out.println(student.toString());
//获取单个无参构造方法并实例化对象
Constructor<?> constructor1 = class1.getConstructor();
Student student1 = (Student) constructor1.newInstance();
System.out.println(student1.toString());
//获取单个无参构造方法并实例化对象
Student student2=(Student) class1.newInstance();
System.out.println(student2.toString());
获取类方法
所有方法,本类方法,有参方法,无参方法,静态方法,私有方法,通用方法
//获取类对象
Class<?> class1= Class.forName("qf.test.test01.Student");
//获取方法
//获取所有的方法:getMethods():公开的所有的方法,包括继承的公开方法
//但是没有私有,默认,保护的方法。
Method[] methods = class1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//获取类中的所有的方法,包含私有,默认,保护的方法,但是
//不包括继承的方法。
Method[] declaredMethods = class1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.toString());
}
//获取单个无参方法
Method methodShow = class1.getMethod("show");
//创建实例对象
Student student =(Student) class1.newInstance();
//使用方法
methodShow.invoke(student);
//获取有参方法
Method show = class1.getMethod("show", String.class,int.class);
show.invoke(student,"上海",12);
//获取私有的方法
Method privateMethod = class1.getDeclaredMethod("privateMethod");
//设置访问权限无效
privateMethod.setAccessible(true);
privateMethod.invoke(student);
//获取静态方法
Method staticMethod = class1.getMethod("staticMethod");
staticMethod.invoke(null);
通用方法:可以调用任何对象的任何方法
//参数:实例对象,方法名,参数类型,实参
public static void invokeAny(Object object,String methodName,Class[] paramsType,Object ...args)throws Exception{
//获取类对象
Class<?> class1 = object.getClass();
//获取方法
Method method = class1.getMethod(methodName, paramsType);
//调用方法
method.invoke(object, args);
}
测试:
Properties properties=new Properties();
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"天气","晴天");
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"地址","北京");
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"小狗","汪汪");
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"小猫","晴天");
System.out.println(properties.toString());
获取类属性
获取属性类,赋值,获取属性值
//创建类对象
Class<?> class1 = Class.forName("qf.test.test01.Student");
//获取所有的属性
Field[] fields = class1.getDeclaredFields();
//遍历
for (Field field : fields) {
System.out.println(field.toString());
}
//获取单个属性
Field name = class1.getDeclaredField("name");
//创建实例对象
Student student =(Student)class1.newInstance();
//设置访问权限
name.setAccessible(true);
System.out.println(name.get(student));
Field age = class1.getDeclaredField("age");
//设置访问权限
age.setAccessible(true);
age.set(student, 14);
System.out.println(age.get(student));
反射优点和缺点
反射实现插件(见我的其他博客文章)
内省
对属性进行操作,有三种
1.反射属性,但是这种暴力反射的行为,不符合封装的特性(所以尽量不要这么使用)
//获取单个属性
Field name = class1.getDeclaredField("name");
//创建实例对象
Student student =(Student)class1.newInstance();
//设置访问权限
name.setAccessible(true);
System.out.println(name.get(student));
Field age = class1.getDeclaredField("age");
//设置访问权限
age.setAccessible(true);
age.set(student, 14);
System.out.println(age.get(student));
2.通过获取getXxx,setXxx方法,去获取或者修改属性的值,但是这种方法很麻烦,而且还必须知道数据的类型。
//创建类对象
Class<?> class1 = Class.forName("qf.test.test08.Person");
//获取getName方法
Method getName = class1.getMethod("getName");
//获取setName方法
Method setName = class1.getMethod("setName", String.class);
//实例化对象
Person person = (Person) class1.newInstance();
//设置属性
setName.invoke(person, "张三");
//获取属性
System.out.println(getName.invoke(person));
3.(重要)内省的方式对属性进行操作。属性:如果类中包含了有返回值的无参getXxx,无返回值的有参SetXxx,boolean类型的isXxx方法,表示类中有xxx属性
首先我们可以通过遍历,得知属性名的类型和属性名
//创建类对象
Class<?> class1 = Class.forName("qf.test.test04.Person");
//根据Introspector工具类创建实体类信息
BeanInfo beanInfo = Introspector.getBeanInfo(class1);
//通过实体类信息创建属性描述符
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍历属性
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
//获取属性名
System.out.println(propertyDescriptor.getName());
//获取属性的类型
System.out.println(propertyDescriptor.getPropertyType());
}
对属性进行操作
//创建类对象
Class<?> class1 = Class.forName("qf.test.test04.Person");
//实例化对象
Person person = (Person)class1.newInstance();
//创建属性描述符
PropertyDescriptor pdName=new PropertyDescriptor("name", class1);
PropertyDescriptor pdAge = new PropertyDescriptor("age", class1);
PropertyDescriptor pdGender = new PropertyDescriptor("gender", class1);
//获得写方法
Method writeName = pdName.getWriteMethod();
writeName.invoke(person, "张三");
Method writeAge = pdAge.getWriteMethod();
writeAge.invoke(person, 19);
Method writeGender = pdGender.getWriteMethod();
writeGender.invoke(person, "男");
//获得读方法
System.out.println(pdName.getReadMethod().invoke(person));
System.out.println(pdAge.getReadMethod().invoke(person));
System.out.println(pdGender.getReadMethod().invoke(person));
设计模式
简单工厂模式
单例模式(全局只有一个)
饿汉式:类一加载,对象就初始化。
缺点:生命周期长,浪费空间。
优点:线程安全
懒汉模式:
public class TestSingleTon {
//防止暴力反射,但是还是能被破解的
private static boolean flag=false;
//在类内部创建一个对象
private static volatile TestSingleTon testSingleTon;
//私有化构造方法
private TestSingleTon(){
if(flag){
throw new RuntimeException("不能暴力反射破解我的构造方法");
}
}
public static TestSingleTon getInstance(){
if(testSingleTon==null){//目的:提高效率,double check 双重检查
synchronized (TestSingleTon.class){
if(testSingleTon==null){
//创建实例
testSingleTon=new TestSingleTon();
flag=true;
}
}
}
return testSingleTon;
}
}
好处:节省空间,生命周期短。缺点:线程不安全
懒汉模式就一定是单例模式吗?不是的,通过暴力反射就可以让它不是单例模式
//创建实例对象
TestSingleTon single=TestSingleTon.getInstance();
System.out.println(single.hashCode());
//创建类对象
Class<?> SingleTon = Class.forName("qf.test.test06.TestSingleTon");
//获得私有构造方法
Constructor<?> testSingleTon = SingleTon.getDeclaredConstructor();
//设置访问模式
testSingleTon.setAccessible(true);
//获得私有属性
Field flag = SingleTon.getDeclaredField("flag");
//设置访问模式
flag.setAccessible(true);
//修改私有属性
flag.set(single, false);
//flag.set(testSingleTon.newInstance(),"false");
TestSingleTon singleTon1=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon1.hashCode());
TestSingleTon singleTon2=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon2.hashCode());
TestSingleTon singleTon3=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon3.hashCode());
TestSingleTon singleTon4=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon4.hashCode());
结果地址都是不一样的。
460141958
356573597
1735600054
21685669
2133927002
new对象时,分四步1,new 2,dup 3,invokespecial 4,a_store,因为指令有优化,可能会造成指令重排,比如这样1,2,4,3,对单线程没有影响,但是对多线程有影响,所以用volitail修饰符。
单列模式的第三种写法(重要)
静态内部类写法
优点:安全 生命周期短,节省空间
public class SingleTon {
//私有化构造方法
private SingleTon(){
}
//在静态内部类中创建对象
public static class Single{
private static final SingleTon singleTon=new SingleTon();
}
//公开方法返回这个对象
public static SingleTon getInstance(){
return Single.singleTon;
}
}
枚举
public enum Gender {
MALE,FEMALE;
private String name="哈哈哈";
public void show(){
System.out.println(name);
}
}
反编译一下
所以说,枚举其实是静态常量
1.枚举常量之间用逗号隔开,最后分后可写也可不写,如果后面有代码,必须加分号。
2.枚举包含私有的构造方法,属性,和普通方法。必须写在常量的后面。
注解(给程序读的)
注释是给程序员看的。
@Deprecated 过时的
只能包含公开属性,定义属性时,可以给默认值。如果注解中只有一个属性名,且是value属性名,可以不用@注解(value=xxx)。可以@注解(xxx)
String name() default "张三";
属性的类型:1.基本类型,2.String类型,3.枚举类型 4.注解类型 5 Class类型 以及上述类型的一维数组。
@Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Annotation1 {
String name() default "哈哈";
int value();
int age();
String gender()default "男";
}
反射得到注解
@Annotation1(value=12,age=24)
public void show(){
System.out.println("我要得到注解信息");
try {
//创建类对象
Class<?> class1 = Class.forName("qf.test.test09.Person");
//获取方法
Method show = class1.getMethod("show");
//获取注解
Annotation1 annotation = show.getAnnotation(Annotation1.class);
//输出属性
System.out.println(annotation.age());
System.out.println(annotation.gender());
System.out.println(annotation.name());
System.out.println(annotation.value());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
用class文件,反编译一下,
注解其实就是接口,里面的属性就是公开抽象方法