「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
前言:注解和反射是一切框架的底层 本文很多是学习狂神视频时做的笔记,感谢狂神,也有加入了自己的理解和思考,希望作为一个整理,也能对大家有所帮助。
注解(java.Annotation)
什么是注解
1.不是程序本身,可以对程序作出解释
2.可以被其他程序(如编译器等)读取
内置注解
@Override 重写
@Deprecated 指这段代码过时了
@SuppressWarning("all") 抑制编译时候的警告信息,需要一个参数
元注解
@Target
class learn extends Object{
@Override
public String toString() {
return "learn{}";
}
@MyAnnotation()
public void say(){
}
}
@Target(value = {ElementType.METHOD})
@interface MyAnnotation{
}
复制代码
例如lombol的源码中标记了运行时才有效果。
Retention 美[rɪˈtenʃn] n.保持 维持;
Retention默认RUNTIME
自定义注解
class Person{
@MyAnno(name="lcg",id = 1,schools = {"bjtu","cn"})
public void say(){
}
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno{
String name() default "xixi";
int id() default -1;
String[] schools();
}
复制代码
反射机制(java.Reflection)
Java是静态语言,因为有了反射机制变成了动态语言
反射:即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
静态语言和动态语言
- 动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、JavaScript、PHP、Python、Erlang。
- 静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++、C#。
PS:C#不是动态语言,但是MS有将.NET支持动态语言的趋势,3.0吸收了一定动态特征,比如 匿名函数,临时类型,临时变量等
动态语言示例:
function Person(name){
this.name=name;
}
Person.prototype.getName=function(){
return this.name;
}
var person=new Person("okok");
alert(person.getName());
person.getName=function(){return "nono"};
alert(person.getName());
复制代码
Java反射机制概述
Demo
package com.company;
public class learn1 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1=Class.forName("com.company.Person");
System.out.println(c1);
Class c2=Class.forName("com.company.Person");
/**
* 一个类只有一个class对象
* 一个类被加载后,类的整个结构多会被封装在class对象中
*/
System.out.println("c1.hashCode"+c1.hashCode());
System.out.println("c2.hashCode"+c2.hashCode());
System.out.println("c1==c2: "+(c2.hashCode()==c1.hashCode()));//true
}
}
class User{
String username;
String password;
int number;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setNumber(int number) {
this.number = number;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public int getNumber() {
return number;
}
public User() {
}
public User(String username, String password, int number) {
this.username = username;
this.password = password;
this.number = number;
}
}
复制代码
输出
class com.company.Person
c1.hashCode692404036
c2.hashCode692404036
c1==c2: true
Process finished with exit code 0
复制代码
理解Class类并获取Class实例
package com.company;
public class learn1 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1=Class.forName("com.company.Person1");
System.out.println(c1);
Class c2=Class.forName("com.company.Person1");
/**
* 一个类只有一个class对象
* 一个类被加载后,类的整个结构多会被封装在class对象中
*/
System.out.println("c1.hashCode"+c1.hashCode());
System.out.println("c2.hashCode"+c2.hashCode());
System.out.println("c1==c2: "+(c2.hashCode()==c1.hashCode()));//true
User user=new User();
//方式一,通过对象获得
Class aClass1=user.getClass();
System.out.println(aClass1.hashCode());
//方式二,通过forname获得
Class aClass2=Class.forName("com.company.User");
System.out.println(aClass2.hashCode());
//方式三,通过类型.class
Class<User> aClass3 = User.class;
System.out.println(aClass3.hashCode());
//方法四:基本内置类型的包装类都有一个Type的属性
Class<Integer> integerClass = Integer.TYPE;
System.out.println(integerClass);
//方式五,获取父类类型
Class<AdminUser> adminUserClass = AdminUser.class;
Class aClass = adminUserClass.getSuperclass();
System.out.println(adminUserClass+" "+adminUserClass.hashCode());
System.out.println(aClass+" "+aClass.hashCode());
}
}
class AdminUser extends User{
@Override
public void login() {
System.out.println("Admin login...");
}
}
class User{
String username;
String password;
int number;
public void login(){
System.out.println("User login...");
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setNumber(int number) {
this.number = number;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public int getNumber() {
return number;
}
public User() {
}
public User(String username, String password, int number) {
this.username = username;
this.password = password;
this.number = number;
}
}
复制代码
类的加载原理与ClassLoader
public class learn3 {
public static void main(String[] args){
A a=new A();
System.out.println(A.m);
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m=300;
}
static int m=100;//如果m在静态代码块前面的话,m就是300
public A(){
System.out.println("A类的无参构造初始化");
}
}
output:
A类静态代码块初始化
A类的无参构造初始化
100
Process finished with exit code 0
复制代码
- 加载到内存,会产生一个类对应Class对象
- 链接,链接结束后m=0
- 初始化
<clinit>(){ System.out.println("A类静态代码块初始化"); m=300; m=100; } 复制代码
分析类初始化
子类没有被加载
因为在链接阶段就已经设置类变量默认初始值了
常量在链接阶段就存入调用类的常量池中了
类加载器的作用(了解)
获取运行时类的结构
有了Class对象,能做什么
动态创建对象
- 直接用字节码文件获取对应实例
// 调用无参构造器 ,若是没有,则会报异常
Object o = clazz.newInstance();
复制代码
- 有带参数的构造函数的类,先获取到其构造对象,再通过该构造方法类获取实例:
//获取构造函数类的对象
Constroctor constroctor = clazz.getConstructor(String.class,Integer.class);
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/275ddf5d2a8f40968a0c56805934c39e~tplv-k3u1fbpfcp-watermark.image?)
// 使用构造器对象的newInstance方法初始化对象
Object obj = constroctor.newInstance("龙哥", 29);
复制代码
调用指定方法
Demo
public class Calculator{
public double add(double score1,double score2){
return score1 + score2;
}
public void print(){
System.out.println("OK");
}
public static double mul(double score1,double score2){
return score1 * score2;
}
}
public class CalculatorTest {
public static void main(String[] args) throws Exception {
//通过类的.class属性获取
Class<Calculator> clz = Calculator.class;
//或者通过类的完整路径获取,这个方法由于不能确定传入的路径是否正确,这个方法会抛ClassNotFoundException
// Class<Calculator> clz = Class.forName("test.Calculator");
//或者new一个实例,然后通过实例的getClass()方法获取
// Calculator s = new Calculator();
// Class<Calculator> clz = s.getClass();
//1. 获取类中带有方法签名的mul方法,getMethod第一个参数为方法名,第二个参数为mul的参数类型数组
Method method = clz.getMethod("mul", new Class[]{double.class,double.class});
//invoke 方法的第一个参数是被调用的对象,这里是静态方法故为null,第二个参数为给将被调用的方法传入的参数
Object result = method.invoke(null, new Object[]{2.0,2.5});
//如果方法mul是私有的private方法,按照上面的方法去调用则会产生异常NoSuchMethodException,这时必须改变其访问属性
//method.setAccessible(true);//私有的方法通过发射可以修改其访问权限
System.out.println(result);//结果为5.0
//2. 获取类中的非静态方法
Method method_2 = clz.getMethod("add", new Class[]{double.class,double.class});
//这是实例方法必须在一个对象上执行
Object result_2 = method_2.invoke(new Calculator(), new Object[]{2.0,2.5});
System.out.println(result_2);//4.5
//3. 获取没有方法签名的方法print
Method method_3 = clz.getMethod("print", new Class[]{});
Object result_3 = method_3.invoke(new Calculator(), null);//result_3为null,该方法不返回结果
}
}
复制代码
获取属性值
package com.company.myp1;
import java.lang.reflect.Field;
public class learn1 {
public static void main(String[] args) throws IllegalAccessException {
Dog d1=new Dog("xixi",11);
Dog d2=new Dog("hehe",22);
//获取某属性
System.out.println(ClassUtil.getField(d1.getClass(),"name"));
//获取某属性值
Object o1=ClassUtil.getPropertyValue(d1,"name");
System.out.println(o1);
//获取全部属性值
Field[] declaredFields = d1.getClass().getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
System.out.println(declaredFields[i].getName()+" "+declaredFields[i].get(d2));
}
}
}
class Dog{
String name;
int age;
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
class ClassUtil {
public static Object getPropertyValue(Object obj, String propertyName) throws IllegalAccessException {
Class<?> Clazz = obj.getClass();
Field field;
if ((field = getField(Clazz, propertyName)) == null)
return null;
field.setAccessible(true);
return field.get(obj);
}
public static Field getField(Class<?> clazz, String propertyName) {
if (clazz == null)
return null;
try {
return clazz.getDeclaredField(propertyName);//getDeclaredFiled 仅能获取类本身的属性成员(包括私有、共有、保护)
} catch (NoSuchFieldException e) {
return getField(clazz.getSuperclass(), propertyName);//getField 仅能获取类(及其父类可以自己测试) public属性成员
}
}
}
复制代码
性能对比分析
反射操作范型
反射操作注解
练习Demo,生成sql语句
package com.company.com.learn1;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class c1 = Class.forName("com.company.com.learn1.Student"); //根据包名获取Class
Annotation[] annotations=c1.getAnnotations();//方法返回此元素上存在的所有注解
for (int i = 0; i < annotations.length; i++) {
System.out.println(annotations[i]);//只获得了注解,想要注解里面的值
}
//获得注解value的值
System.out.println("===============");
TableName tableName = (TableName) c1.getAnnotation(TableName.class);
System.out.println(tableName.value());
//获得类指定的注解
System.out.println("===============");
Field f=c1.getDeclaredField("name");
FieldName fieldName=f.getAnnotation(FieldName.class);
System.out.println(fieldName.type());
System.out.println(fieldName.lengtn());
System.out.println(fieldName.columnName());
//获得所有字段的列名,类型和长度
System.out.println("===============");
Field[] fieldNames = c1.getDeclaredFields();
for (int i = 0; i < fieldNames.length; i++) {
FieldName annotation = fieldNames[i].getAnnotation(FieldName.class);
System.out.println(annotation.columnName()+" "+annotation.type()+" "+annotation.lengtn());
}
//获得所有字段的属性名,属性值和注解
System.out.println("===============");
Student s=new Student(1001,18,"小明");
//s.getClass().getAnnotation();
TableName annotation = s.getClass().getAnnotation(TableName.class);
String mytableName=annotation.value();
Field[] declaredFields = s.getClass().getDeclaredFields();
StringBuffer value=new StringBuffer();
StringBuffer column=new StringBuffer();
for (int i = 0; i < declaredFields.length; i++) {
declaredFields[i].setAccessible(true);
System.out.println("属性名:" + declaredFields[i].getName() + " 属性值:" + declaredFields[i].get(s)+" 注解columnname "+ declaredFields[i].getAnnotation(FieldName.class).columnName());
value.append(declaredFields[i].get(s));
column.append(declaredFields[i].getAnnotation(FieldName.class).columnName());
if(i!=declaredFields.length-1){
value.append(" , ");
column.append(" , ");
}
}
//模仿自己生成sql
System.out.println("===============");
System.out.println("利用反射和注解生成sql语句:");
String sql="INSERT INTO "+mytableName+" ( "+column+" ) VALUES "+" ( "+value+" )";
System.out.println(sql);
}
}
@TableName("db_student")
class Student{
@FieldName(columnName = "db_id",type = "int",lengtn = 10)
private int id;
@FieldName(columnName = "db_age",type = "int",lengtn = 10)
private int age;
@FieldName(columnName = "db_name",type = "varchar",lengtn = 30)
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public Student() {
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableName{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldName{
String columnName();
String type();
int lengtn();
}
复制代码
输出:
@com.company.com.learn1.TableName(value=db_student)
===============
db_student
===============
varchar
30
db_name
===============
db_id int 10
db_age int 10
db_name varchar 30
===============
属性名:id 属性值:1001 注解columnname db_id
属性名:age 属性值:18 注解columnname db_age
属性名:name 属性值:小明 注解columnname db_name
===============
利用反射和注解生成sql语句:
INSERT INTO db_student ( db_id , db_age , db_name ) VALUES ( 1001 , 18 , 小明 )
Process finished with exit code 0
复制代码