Java笔记(反射、动态代理、注解)
反射
概念
获取class对象的三种方式
获取一个类的全类名
-
全类名 :包名 + 类名
-
获取方式如下
-
然后直接在双引号里面粘贴
获取构造方法
- Declared是表示要获取所有类型的构造方法 ,不管是不是被public修饰
getConstructors
- 获取所有公共==(被Public修饰)==构造方法
getDeclaredConstructors
- 获取所有构造方法(包括私有(private修饰)的或者(protected修饰的))
getDeclaredConstructor(形参类型.class)
- 获取特定的构造方法
指定有参构造创建对象
获取public修饰的构造方法
-
//获取class对象之后 ,用这个对象实例化 Constructor c1 = clazz.getConstructor(String.class, int.class, String.class); Car car1 = (Car)c1.newInstance("夏利", 10, "红色"); System.out.println(car1+" 参数 "+car1.getName()+" "+car1.getAge()+" "+car1.getColor());
获取private修饰的构造方法(暴力反射)
私有的构造方法
用的是getDeclaredConstructor ,并且还要设置setAttribute(true)
Modifiers
- 获取构造方法的权限修饰符(以整数形式体现)
getParameters
- 获取某个构造方法里面的全部参数
- 下面报错的原因是获取的方法是私有(private)的 , 不能在这个类里面创建对象 , 所以需要暴力反射
- 解决办法
暴力反射
- 暴力反射 , 需要Construct类点上setAttribute设置为true , 表示临时解除private权限修饰 , 获得访问权限
- 当需要修改私有的构造方法的对象或者成员变量里面的值的时候需要用暴力反射
获取成员变量
getFields
- 获取所有公共的成员变量
getDeclaredFields
- 获取所有成员变量
getDeclaredField(“变量名字”)
- 获取单个的成员变量
getModifiers
- 获取某个成员变量的权限修饰符
- 获取某特定成员变量存储的属性 , 而不是所有变量
- 此外还可以修改对象里面的值
- 表示把s对象的name属性的name值改成"lisi"
获取成员方法
getMethods
- 注意构造方法不同于成员方法
- 构造方法主要用于构造某个类的对象 , 而成员方法是用来实现某个功能的
invoke
-
引用\援引
-
要用调用者s去调用eat方法
-
m是获得了指定的单一方法(eat)
反射的作用
- 获取一个类里面的所有信息 . 获取到之后 , 再执行其他的业务逻辑
- 结合配置文件 , 动态的创建对象并调用方法
练习一
//实体类
package com.LiBai.myReflection.myReflect3;
public class Teacher {
private String name;
private int salary;
private int age;
private double height;
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", salary=" + salary +
", age=" + age +
", height=" + height +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return (float) height;
}
public void setHeight(float height) {
this.height = height;
}
public Teacher(String name, int salary, int age, double height) {
this.name = name;
this.salary = salary;
this.age = age;
this.height = height;
}
public Teacher(String name, int salary) {
this.name = name;
this.salary = salary;
}
public Teacher() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
//测试类
public class reflectTest {
public static void main(String[] args) throws IllegalAccessException, IOException {
Teacher t = new Teacher("小A", 10000, 23, 167.5);
saveObject(t);
}
public static void saveObject(Object obj) throws IllegalAccessException, IOException {
//1.获取字节码文件的对象,因为已经有了类的对象,所以直接就可以对象点上getClass
Class<?> clazz = obj.getClass();
//2.获取IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("src/a.txt"));
//3.获取所有的成员变量,因为不知道是不是私有的,所以用Declared
Field[] fields = clazz.getDeclaredFields();
//遍历成员变量
for (Field field : fields) {
//因为不知道成员变量是否被private修饰(有无访问权限),所以用暴力反射
field.setAccessible(true);
//获取成员变量的名字
String name = field.getName();
//获取成员变量的值
Object value = field.get(obj);
//写出数据
bw.write(name+"="+value);
//System.out.println(name+"="+value);
//写一行换一行
bw.newLine();
}
//关流
bw.close();
}
}
练习二
-
配置文件即.properties文件
-
读取配置文件的信息
//propertites文件
classname=com.LiBai.myReflection.myReflect4.Teacher
method=teach
public class Teacher {
private String name;
private double salary;
public void teach(){
System.out.println("老师在教书!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Teacher() {
}
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
}
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void study(){
System.out.println("学生在学习!");
}
}
public class reflect {
//读取配置文件的信息
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//读取配置文件中的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("src/prop.properties");
prop.load(fis);
System.out.println(prop);//{classname=com.LiBai.myReflection.myReflect4.Student, method=study}
//获取全类名中的信息
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");
System.out.println(className);//com.LiBai.myReflection.myReflect4.Student
System.out.println(methodName);//study
//运用反射去创建对象并运行方法
Class<?> clazz = Class.forName(className);
//获取构造方法(暴力反射)
//调用的是空参构造,但防止空参构造被私有化,所以用Declared
Constructor<?> con = clazz.getDeclaredConstructor();
Object o = con.newInstance();
System.out.println(o);//Student{name='null', age=0}\
//获取成员方法并运行
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(o);//让o去调用这个method
}
}
- 以后再想要使用另一个类里面的方法,就只需要改一下配置文件里面的信息就可以 , 例如本例就把配置文件里面那个classname改一次,method改一下就可以了
总结
动态代理
概念
- 想要增加源码的功能 , 直接修改源码叫做侵入式修改
- 动态代理可以避免侵入式修改代码
如何为Java对象创建一个代理对象
大练习
public class bigStar implements star{
private String name;
public bigStar() {
}
public bigStar(String name) {
this.name = name;
}
//唱歌
//this.name是获取这个方法之外的name , 也就是成员变量的name
//注意this.name和name不一样,前面表示成员变量里面的name,后面表示歌名name
@Override
public String sing(String name){
System.out.println(this.name+"正在唱"+name);
return "谢谢";
}
//跳舞
@Override
public void dance(){
System.out.println(this.name+"正在跳舞");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface star {
//定义接口类来代理方法
//删掉方法体,因为在接口里面都是抽象方法
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
//类的作用:创建一个代理
public class proxyUtil {
/*
* 方法的作用:
* 给一个明星的对象创建一个代理
* 形参:被代理的明星对象
* 返回值:方法的返回值是一个代理
* 因为代理也要实现star的接口
*
* 需求:
* 外面的人想要大明星唱一首歌
* 1.获取代理的对象
* 代理对象=proxyUtil.creatProxy
* 2.再调用代理的唱歌方法
* 代理对象.唱歌的方法();
* */
public static star creatProxy(bigStar bigStar){
//java.lang.reflect.Proxy类:提供了为对象产生代理的方法,且该类静态,可直接类名调用方法
//参数一:类加载器,就是一个把字节码文件加载到内存当中的工具,注意是把当前的加载到内存当中,并且再让他把代理加载到内存当中
//参数二:指定接口,这些接口用来指定生成的代理长什么样,也就是有哪些方法,而代码当中以数组方式体现
// 也就是我们生成的代理可以代理star接口中所有的方法
//参数三:用来指定生成的代理对象要干什么事
star star = (dynamicproxy.star) Proxy.newProxyInstance(
proxyUtil.class.getClassLoader(),
new Class[]{star.class},//把接口的字节码放在数组当中
new InvocationHandler() {
//这个类才是真正放代理要干的事情
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:要运行的方法 sing
* 参数三:调用sing方法时传递的实参
* 在定义sing方法时要传入歌曲的名字
* */
if ("sing".equals(method.getName())){
//如果当前运行的是唱歌的方法
System.out.println("准备话筒,收钱");
}else if ("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式,调用大明星里面唱歌或者跳舞的方法
//反射 括号里是调用方法的对象和需要传递的参数
return method.invoke(bigStar,args);
}
});
return star;
}
}
public class Test {
//需求:让大明星唱歌
public static void main(String[] args) {
//先创建一个明星对象
bigStar bigStar = new bigStar("鸡哥");
//1.获取代理的对象
star proxy = proxyUtil.creatProxy(bigStar);
//2.调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
//3.调用跳舞的方法
proxy.dance();
}
}
总结
注解
概念
-
概念:Java注解又称Java标注,是在 JDK5 时引入的新特性,注解(也被称为元数据)。Java注解它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。
-
应用:
1生成文档这是最常见的,也是java 最早提供的注解;
2.在编译时进行格式检查,如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
3.跟踪代码依赖性,实现替代配置文件功能,比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置;
4.在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口,可以在反射中解析并使用 Annotation。
注解(Override Deprecated SuppressWarning)
- Deprecated 是在想要定义的方法的头上加的
- SuppressWarning是在要调用已经定义好的类或者方法处加的
- 看到调用test1的地方加上了横线
- 看到在调用处加上SuppressWarning之后,test1的横线消失了
元注解
-
元注解就是解释注解的注解,它注解的对象是我们前面介绍的注解,如:@Override、@Deprecated 、@SuppressWarnings
public enum ElementType {
TYPE, // 类、接口、枚举类
FIELD, // 成员变量(包括:枚举常量)
METHOD, // 成员方法
PARAMETER, // 方法参数
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类
PACKAGE, // 可用于修饰:包
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}
自定义注解
public class test {
//自定义注解
//注意看下面的注解只需要规定age,不需要规定其他参数,因为其他参数在定义注解的时候就有默认值
@myAnnotation(age=18)
public void test01(){
}
@myAnnotation2(value = "小李")
public void test02(){
}
}
//目标作用域:类和方法上面
@Target({ElementType.TYPE,ElementType.METHOD})
//存活时间:runtime表示注解可以保留到程序运行的时候
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
//注解的参数:参数类型+参数名();
//注意后面加括号并不代表他是一个方法,只是myAnnotation这个注解的参数的特殊写法
String name() default "小明";
int age();
//default表示默认值,不加default的话在使用注解的时候要自行加上参数
int id() default -1;
//这是定义了一个参数为数组
String[] schools() default {"清华大学","北京大学"};
}
//目标作用域:类和方法上面
@Target({ElementType.TYPE,ElementType.METHOD})
//存活时间:runtime表示注解可以保留到程序运行的时候
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation2{
String value();
}
- 自定义注解练习
- 左边是自定义的注解类
- 右边是注解的营业类
- 元注解练习
- target
-
上图表示定义了一个注解类,并且被target修饰且只能用在type(类)里面 , 那么这个MyTest3注解了就只能修饰类 , 不能修饰成员方法和成员变量
-
retention
-
源码(刚刚写好的java代码)(Source)->编译时(.class文件)(默认值)->运行时(runtime开发时常用)
注解的解析
解析注解练习
- 第一行得到类的对象
- 第二行得到类中方法的对象
- getDeclaredMethod括号里面的参数是方法的名字
练习一:解析类的注解
- 如果在maven项目里面使用test注解,需要现在test所在类里面补充main方法
//先定义一个注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定义使用范围
@Target({ElementType.TYPE,ElementType.METHOD})
//定义存活时间
@Retention(RetentionPolicy.RUNTIME)
public @interface Mytest4 {
//定义参数
String value();
double aaa() default 100;
//数组元素
String[] bbb();
}
//定义一个使用注解的类
@Mytest4(value = "蜘蛛精",aaa = 99.5,bbb={"123","456"})
public class Demo {
@Mytest4(value = "哈哈哈",aaa = 1999.9,bbb={"好好学习","天天向上"})
public void test1(){
}
}
//测试类
//获取被注解修饰的类的注解信息
import org.junit.Test;
import java.util.Arrays;
public class AnnotationTest3 {
public static void main(String[] args) {
}
@Test
public void parseClass() throws NoSuchMethodException {
//先要获取类的对象
Class c = Demo.class;
//解析类上的注解
//判断类上是否存在那个注解
if (c.isAnnotationPresent(Mytest4.class)){
Mytest4 mytest4 = (Mytest4) c.getDeclaredAnnotation(Mytest4.class);
System.out.println(mytest4.value());
System.out.println(mytest4.aaa());
System.out.println(Arrays.toString(mytest4.bbb()));
}
}
}
练习二:解析方法的注解
- 注意一定要先获取方法的对象再获取这个方法的注解
@Test
public void parseMethod() throws NoSuchMethodException {
//先获取方法所在类的对象,再用这个类调用这个要获取的方法
Class c = Demo.class;
//getDeclaredMethod是获取特定的方法
//得到方法的对象
Method test = c.getDeclaredMethod("test1");
//开始获取方法的注解
//注意括号里面写的是定义注解的所在类,而不是注解的应用类
if (test.isAnnotationPresent(Mytest4.class)){
//注意下面括号里获取的还是定义注解的所在类,通过这个类再去获取应用到方法(test1)上面的注解信息 ,test是test1方法的对象
Mytest4 mytest4 = test.getDeclaredAnnotation(Mytest4.class);
System.out.println(mytest4.aaa());
System.out.println(mytest4.value());
String string = Arrays.toString(mytest4.bbb());
System.out.println(string);
}
}
}
模拟JUnit框架
//规定一个注解类
package AnnotationTest2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定义注解执行范围
@Target(ElementType.METHOD)//规定注解在方法上有效
@Retention(RetentionPolicy.RUNTIME)//规定在运行时有效
public @interface MyTest {
}
//测试类
package AnnotationTest2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class AnnotationTest {
//定义一些测试方法 , 其中一些没有注解修饰
public void test1(){
System.out.println("======test1======");
}
@MyTest
public void test2(){
System.out.println("======test2======");
}
public void test3(){
System.out.println("======test3======");
}
@MyTest
public void test4(){
System.out.println("======test4======");
}
//要达到的目的是运行有注解的方法,不运行没有注解的方法
//这里不能直接右键运行,因为没有运行的菜单,JUnit右键可以直接运行,但是现在是模拟JUnit框架,所以需要自己设计一个运行的方法
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
//对应invoke方法
AnnotationTest a = new AnnotationTest();
//获取到这个类里面所有的测试方法,遍历查看有没有注解修饰,如果有就运行,没有就不运行
//获得类对象
Class c = AnnotationTest.class;
//获得对象里面的所有方法,自动返回数组
Method[] methods = c.getDeclaredMethods();
//遍历这个数组
for (Method method : methods) {
//先判断是否有注解
//用每个遍历出来的方法去调用判断是否存在注解
if (method.isAnnotationPresent(MyTest.class)){
//如果存在MyTest修饰的注解,就运行,使用invoke方法调用要运行的方法,括号里面是调用者
method.invoke(a);
}
}
}
}
标签:Java,String,void,笔记,class,注解,public,name
From: https://blog.csdn.net/weixin_73749601/article/details/144547741