若要学习别人写好的开源的框架(这些框架为了保持通用,在底层大量使用了反射和注解相关的知识和内容)所以,对反射注解的学习非常重要!
反射非常强大,甚至可以操作类的私有成员
类 + 包 + 属性
反射是一个非常底层的知识
在类的基础上,又可以抽取出一层,有6个(可以描述任何的类,和类中间的成员):
@Override
这6个构在一起,就是反射的机制
就像File file = new File("路径");
,file对象(在内存中)通过路径和真实的文件产(在硬盘中)生映射关系
获取Class的三种方式:
Class c = Class.forName("包名.类名");
//要写类的全名Class c = 类名.class;
//类必须要存在Class c = 对象.getClass();
//这是Object中的方法类有自己的结构:权限修饰符,特征修饰符,类名字,继承,实现
Class常用方法:
getModifiers()
返回修饰符(包括权限修饰符、特征修饰符),而且返回值是整型。getName()
返回值String,返回类全名(包名.类名)getSimpleName()
返回值String,只返回类名(不带包名)getPackage()
返回值是Package,返回类所在的包getSuperClass()
返回值Class,获取超类(即父类)getInterface()
返回值是Class[],获取当前类的所有父亲接口getClasses()
返回值Class[],返回内部类通过以上七个方法,可以把一个类的结构描述清楚
但是我们所要的不是类啊,而是类中的成员(属性和方法)
Field f = c.getFileld("属性名");
c是Class类型,返回值是Field,用来获取类的属性(只能是公有的)getModifiers()
获取属性的修饰符getName()
获取属性名getType()
获取属性的类型,返回值是Class我们获取属性是为了操作属性(存值或取值);必须要有类的对象,才能对属性值进行操作
get(对象)
获取属性
set(对象, 值)
设置属性不知道属性名,索性把公有属性全取出来
getFields()
,返回值是Field[]反射很强,私有属性也可以操作(取值、存值)哦
Field[] f = c.getDeclaredField("属性名");
c是Class类型,返回值是Field,用来获取类的属性(包括私有)getDeclaredFields()
,返回值Field[]
setAccessible(true)
getMethod("方法名", Class...参数类型)
,用来获取类(包括父类)的公有方法,返回值是Method
比如说有一个方法(有重载)
public void sleep(){}
public void sleep(int time){}
那么:
//c是上述方法所在类的Class
Method m1 = c.getMethod("sleep");//获取无参的
Method m2 = c.getMethod("sleep", int.class);//获取带参数的
int modifiers = m.getModifiers();
Class mrt = m.getReturnType();
String name = m.getName();
Class[] mpts = m.getParameterTypes();
Class[] mets = m.getExceptionTypes();
上面方法使我们可以获取方法的结构,但是重要的是如何操作方法
Object result = invoke(对象, 执行方法需要传递的参数...);
调用方法,也可以接收返回值不知道方法名,索性把公有方法全取出来
Method[] ms = c.getMethods()
获取所有公有的方法(包括自己类的和父类的)操作私有方法
Method ms = c.getDeclaredMethod("方法名", Class...参数类型)
用来获取类方法(包括私有)Method[] ms = c.getDeclaredMethods()
setAccessible(true)
获取构造方法
Constructor con = c.getConstructor(Class...参数类型);//自己类+父类的公有构造方法
Constructor con = c.getDeclaredConstructor(Class...参数类型);//自己类的构造方法(包括私有)
Constructor[] cons = clazz.getConstructors();//自己类+父类的全部公有构造方法
Constructor[] cons = clazz.getDeclaredConstructors();//自己类的全部构造方法(包括私有)
Constructor类中的常用方法
int modifiers = con.getModifiers();
String name = con.getName();
Class[] cpts = con.getParameterTypes();
Class[] cets = con.getExceptionTypes();
//执行无参构造方法
con.newInstance();//返回值是Object
//执行带参数构造方法
con.newInstance(参数...);
//执行私有构造方法之前,需要
con.setAccessible(true);
在学习Java反射时,不论是通过何种方式都不约而同的提到,可以通过反射修改String的内容
但是,随着Java版本的变更,String底层也屡经修改
public final class String{
private final char[] value;
}
public final class String{
private final byte[] value;
private final byte coder;
}
import java.lang.reflect.Field;
public class ChangeStringValue {
public static void main(String[] args) throws Exception{
String str = new String("zgh");
System.out.println(str);//zgh
//反射的技术 可以获取私有属性,还可以操作私有属性
//1.获取String类对应的那个Class
Class c = str.getClass();
//2.通过c获取String中的value属性
Field field =c.getDeclaredField("value");
//3.设置私有属性可以修改
field.setAccessible(true);
//4.获取value属性里面的值
char[] t = (char[]) field.get(str);
//5.通过t的地址引用 找到真实String对象中的数值
// 修改数组内的每一个元素
t[0] = 'Z'; t[1] = 'G'; t[2] = 'H';
//输出修改后的str
System.out.println(str);//ZGH
}
}
import java.lang.reflect.Field;
public class ChangeStringValue {
public static void main(String[] args) throws Exception{
String str = new String("zgh");
System.out.println(str);//zgh
//反射的技术 可以获取私有属性,还可以操作私有属性
//1.获取String类对应的那个Class
Class c = str.getClass();
//2.通过c获取String中的value属性
Field field =c.getDeclaredField("value");
//3.设置私有属性可以修改
field.setAccessible(true);
//4.获取value属性里面的值
byte[] t = (byte[]) field.get(str);
//5.通过t的地址引用 找到真实String对象中的数值
// 修改数组内的每一个元素
t[0] = (byte)'Z'; t[1] = (byte)'G'; t[2] = (byte)'H';
//输出修改后的str
System.out.println(str);//ZGH
}
}
利用反射设计一个小工具(替代我们自己创建对象的过程)String --> 类 --> 对象
这是Spring(开源的一个框架)的思想,Spring有两个非常重要的思想(IOC、AOP)
public class MySpring {
public Object getBean(String className){//参数为类全名
Object obj = null;
try {
Class c = Class.forName(className);
obj = c.getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
以下代码的DI体现在:给MVC分层思想的domian层中的实体类自动注入属性值
//导包...
public class MySpring {
public Object getBean(String className){//参数为类全名
Object obj = null;
try {
Class c = Class.forName(className);
obj = c.getDeclaredConstructor().newInstance();
//把所有的属性都找到
Field[] fs = c.getDeclaredFields();
for(Field field:fs){
//1.获取属性名
String fieldName = field.getName();
//2.手动的拼接串---setXXX方法
String first = fieldName.substring(0,1).toUpperCase();
String other = fieldName.substring(1);
StringBuilder builder = new StringBuilder("set");
builder.append(first);
builder.append(other);
//3.获取属性的类型,为了找寻setXXX方法时传递参数
Class fieldTypeClass = field.getType();
//4.通过处理好的setXXX方法名,找寻类中的setXXX方法
Method m = c.getMethod(builder.toString(), fieldTypeClass);
//5.赋值(读取配置文件 或者 注解)
m.invoke(obj, 值);
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
如果属性值是通过用户在控制台输入,并且 DI 注入的属性值的类型是除了 char 的其它七个基本类型或者String,可以将就着这么写
//5.赋值(用户在控制台进行输入)
System.out.println("请给"+ fieldName + "属性赋值");
String value = input.nextLine();
//补充:八个基本类型的包装类有七个都含有带String的构造方法,
//除了Character都有参数为String的构造方法
Constructor con = fieldTypeClass.getConstructor(String.class);
m.invoke(obj, con.newInstance(value));
//导包...
public class MySpring {
public <T>T getBean(String className){//参数为类全名
T obj = null;
try {
Class c = Class.forName(className);
obj = (T)c.getDeclaredConstructor().newInstance();
Field[] fs = c.getDeclaredFields();
for(Field field:fs){
String fieldName = field.getName();
String first = fieldName.substring(0,1).toUpperCase();
String other = fieldName.substring(1);
StringBuilder builder = new StringBuilder("set");
builder.append(first);
builder.append(other);
Class fieldTypeClass = field.getType();
Method m = c.getMethod(builder.toString(), fieldTypeClass);
m.invoke(obj, 值);
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
//导包...
public class MySpring {
//属性 为了存储所有被管理的对象
private static HashMap<String,Object> beanBox = new HashMap<>();
public static <T>T getBean(String className){//参数为类全名
T obj = null;
try {
Class c = Class.forName(className);
//1.直接从beanBox集合中获取
obj = (T)beanBox.get(className);
//2.如果obj是null,证明之前没有创建过对象
if (obj == null){
obj = (T)c.getDeclaredConstructor().newInstance();
beanBox.put(className, obj);
}
Field[] fs = c.getDeclaredFields();
for(Field field:fs){
String fieldName = field.getName();
String first = fieldName.substring(0,1).toUpperCase();
String other = fieldName.substring(1);
StringBuilder builder = new StringBuilder("set");
builder.append(first);
builder.append(other);
Class fieldTypeClass = field.getType();
Method m = c.getMethod(builder.toString(), fieldTypeClass);
m.invoke(obj, 值);
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
注解(Annotation)与注释不同
注释的写法:
//
/* */
/** */
@XXX
@XXX[一些信息]
【举个例子】在平时用Idea写Java时,会遇到一些方法上面有横线提示(这种方法已过时,虽然也可以用,但是已经被更好的方法替代了),比如:
那编辑器怎么知道这些方法是过时的呢?
随便打开一个,看一下源码:
@Deprecated
仅仅是充当注释的作用:提示这个方法已经过时
【举个例子】重写和重载容易弄错,如果在方法上面加上注解@Override
,就可以检测出自己写的是否是自己想写的重写方法,
这种注解还可以帮我们做代码的检测(减少写代码出错的次数)
@Deprecated
用来说明方法是废弃的@Override
用来做代码的检测,检测此方法是否是一个重写方法@SuppressWarnings(信息)
去除警告信息
信息
的类型是String[]
,信息
的类型还可以就是String
【举个例子】
在Idea中,str因为没有被调用过,str下面有波浪号警告;其实可以用注解@SuppressWarnings(信息)
把这种警告去掉。
信息有很多种值,例如unused
,就可以把变量定义后未被使用的警告去掉
因为@SuppressWarnings(信息)
中的信息
只写了一个,可以把大括号去掉
但是这玩意,尽量不要用
@SuppressWarnings(信息)
,信息
的值:
unused
变量定义后未被使用serial
类实现了序列化接口,不添加序列号IDrawtypes
集合没有定义泛型deprecation
过时的方法unchecked
出现了泛型的问题,可以不检测all
所有的警告都不管,包含了以上所有这些警告除了unchecked
都可以通过编码规范给去掉,所以不建议使用
信息不能随便写 信息的类型只能是如下的类型:
通过@interface
定义一个新的注解类型
发现写法和接口非常相似(可以利用接口的特点来记忆注解)
我们自己定义的注解如果想要拿来使用
元注解通常有这么几类:
@Target
描述当前的这个注解可以放置在哪里写
@Retention
retention是保留的意思,描述当前的注解存在什么作用域中
@Inherited
描述当前这个注解能否被子类对象继承
它不需要携带信息的,就这么写,代表自定义的注解可以被子类继承
@Document
描述这个注解能否被文档所记录,不太常用
1. 注解的方法做事,将我们传递给他的参数搬运走了,给了别人(以后解析注解的人)
2. 当注解只有一个方法,而且方法名为value
的时候,用的时候方法名可以省略不写了
利用反射技术解析注解(包括注解内部携带的信息)
【举个例子】自定义一个注解MyAnnotation
,并在某方法的属性上使用
`String[] values = ((MyAnnotation)a).value();
文章前面只使用了反射来完成 IOC+DI,其实并不完善,DI 注入属性值时只写了使用Scanner控制台输入,其实并不好。
在这里使用注解来完成 DI 注入属性值的功能: