注解和反射
这里写目录标题
1、常见内置注解
- @Override:用来修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
- @Deprecated:此注解可以用来修饰方法,属性,类,表示不鼓励程序元使用这样的元素,通常因为它很危险或者存在更好的选择
- @SuppressWarnings:用来抑制编译时的警告信息
- 与前面注解有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好的,我么你选择性使用就好了
- @SuppressWarnings(“all”)
- @SuppressWarnings(“unchecked”)
- @SuppressWarnings(“value = {“unchecked”,“deprecation”}”)
- 等等
- 与前面注解有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好的,我么你选择性使用就好了
2、元注解
概念:元注解的作用就是负责解释其他注解的注解,java定义了四个mata-annotation类型,他们被用来提过对其他annotation类型做说明
- @Target:用于描述注解的使用范围
@Target({ElementType.METHOD,ElementType.TYPE})//在方法和类上可以加自定义注解myAnntation
//自定义注解
@interface myAnntation{}
- @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期
//runtime(运行期间)>class(类)>sources(源码)
@Retention(value = RetentionPolicy.SOURCE)
//自定义注解
@interface myAnntation{}
- @Document:说明该注解将被包含在javadoc中
//表示是否将我们的注解生成在javadoc中
@Documented
//自定义注解
@interface myAnntation{}
- @Inherited:说明子类可以继承父类中的该注解
//子类可以继承父类的注解
@Inherited
//自定义注解
@interface myAnntation{}
3、自定义注解
使用@interface自定义注解时,自动集成了java.long.annotation.Annotation接口
分析:
-
@interface用来声明一个注解
-
格式:public @interface 注解名{定义内筒}
-
其中每一个方法实际上就是声明了一个配置参数
-
方法的名称就是参数的名称
-
返回值类型就是参数的类型(返回值只能是基本类型、Class、Strin、enum)
-
可以用default来声明参数的默认值
-
如果只有一个参数成员,一般参数名为value
-
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
例子
//自定义注解
public class Annotation02 {
//如果不传递没有默认值的参数,则会报错
@Demo1(name="morant")
public void test(){}
@Demo2("如果只有一个参数,并且参数的名字为vule,则可以省略value")
public void test1(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Demo1{
//注解参数: 参数类型 + 参数名();
String name();
int age() default 0;
int id() default -1;//如果默认值为-1.表示不存在
String [] school() default {"哔哩哔哩大学"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Demo2{
//注解参数: 参数类型 + 参数名();
//只有一个参数 建议使用value作为参数名
String value();
}
4、反射(Reflection)
概念:Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API获取任何累的内部信息,并能直接操作任意对象的内部属性及方法
1、入门:获取一个反射对象
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取class对象
Class c1 = Class.forName("cn.morant.Reflection.sutdent");
System.out.println(c1);
Class c2 = Class.forName("cn.morant.Reflection.sutdent");
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构都会被封装在Class对象中
System.out.println(c1==c2);
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
}
}
//实体类
class sutdent{
private String name;
private int age;
public sutdent() {
}
public sutdent(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "sutdent{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
sutdent sutdent = (sutdent) o;
return age == sutdent.age && Objects.equals(name, sutdent.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
结论:
- 一个类在内存中只有一个Class对象
- 一个类被加载后,类的整个结构都会被封装在Class对象中
2、获取Class对象的5种方法
public class Reflection2 {
public static void main(String[] args) throws ClassNotFoundException {
// 1.通过Class.forname("包名")获取
Class c1 = Class.forName("cn.morant.Reflection.Student");
//2.通过对象.getClass()获得
Student student = new Student();
Class c2 = student.getClass();
//3.通过类名.class获得
Class c3 = Student.class;
//4.基本内置类型的包装类的TYPE属性可以获得
Class c4 = Integer.TYPE;
//5.子类的class对象可以获得父类的class
Class c5 = student.getClass().getSuperclass();
}
}
class Person{
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Student extends Person{
String name ="学生";
}
2、所有类型的Class属性
哪些类型可以有Class属性?
- class:外部类,成员(成员内部类、静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
例子:
public static void main(String[] args) {
Class c1 = Object.class;//类
Class c2 = Comparable.class;//接口类型
Class c3 = String[].class;//字符串数组类型
Class c4 = int[][].class;//二位int数组类型
Class c5 = Override.class;//注解类型
Class c6 = ElementType.class;//枚举类型
Class c7 = Integer.class;//基本数据类型的封装
Class c8 = void.class;//void
Class c9 = Class.class;//类
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
3、Java加载内存分析
4、分析类的初始化
5、类加载器的作用
6、获取类运行时的结构
前提:
Person person = new Person();Class c1 = person.getClass();
- 获取类的名字
String name = c1.getName();//包名+类名String simpleName = c1.getSimpleName();//类名System.out.println(name);System.out.println(simpleName);
- 获取类的所有方法和指定方法
//获取本类包括父类的所有
public的方法Method[] methods = c1.getMethods();
//获取本类包括父类的所有public的方法
for (Method method : methods) {
System.out.println(method);
}
//获取本类自身的所有(包括public 和 private修饰的)的方法
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
//获取本类自身或者父类的public修饰的方法 参数为方法名、参数类型的class对象
Method go = c1.getMethod("go", null);System.out.println(go);
//获取本类自身的方法,参数为方法名、参数类型的class对象
Method go1 = c1.getDeclaredMethod("go", null);System.out.println(go1);
- 获取类的所有属性和指定属性
//获取本类的public属性
Field[] fields = c1.getFields();for (Field field : fields) {
System.out.println(field);
}
//获取蓓蕾的所有属性
Field[] declaredFields = c1.getDeclaredFields();for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
- 获取本类的构造器
//获取本类的公有构造函数
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//获取本类的所有构造函数
for (Constructor declaredConstructor : c1.getDeclaredConstructors()) {
System.out.println(declaredConstructor);
}
7、通过反射动态创建对象
准备的类:
class Demo{
private String name; //无惨构造
public Demo() { } //有参构造
public Demo(String name) {
this.name = name;
}
private void go(String name){
System.out.println(name+"正在走路");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Demo{" + "name='" + name + '\'' + '}';
}
}
- 通过获得的class对象创建对象(注意:①对象必须存在无惨构造方法,②构造方法的修饰符如果是private,则需要用setAccessiable方法关闭安全检查)
//通过Class来动态创建对象
Class c1 = Class.forName("cn.morant.Reflection.Demo");
//包名+类名
Demo demo = (Demo) c1.newInstance();
//强制类型转换,默认这种方式调用无惨构造
System.out.println(demo);
- 通过获得的构造其创建对象
Class c1 = Class.forName("cn.morant.Reflection.Demo");//包名+类名
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class);
Demo demo =(Demo) declaredConstructor.newInstance("morant");System.out.println(demo);
- 通过反射动态调用操作属性
//通过反射动态调用操作属性
Class c1 = Class.forName("cn.morant.Reflection.Demo");//包名+类名
//先用反射创建一个实例对象
Demo demo = (Demo) c1.newInstance();
Field name = c1.getDeclaredField("name");
//私有属性应该先设置关闭安全监测
name.setAccessible(true);
//第一个参数为对象,第二个参数为方法的参数值
name.set(demo,"莫哥");
System.out.println(demo.getName());
- 通过反射动态调用对象的方法
//通过反射动态调用对象的方法
Class c1 = Class.forName("cn.morant.Reflection.Demo");//包名+类名
//先用反射创建一个实例对象
Demo demo = (Demo) c1.newInstance();
Method go = c1.getDeclaredMethod("go", String.class);
//私有方法应该先设置关闭安全监测
go.setAccessible(true);
//通过invoke激活方法,第一个参数为对象,第二个参数为方法的参数值
go.invoke(demo,"morant");
8、性能对比分析
- 普通方法(通过对象调用方法)
//普通方式调用
public static void test01(){
Demo demo = new Demo();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
//获取10亿次对象的名字
demo.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式调用花费了"+(endTime-startTime)+"ms");
}
- 反射方式调用对象的方法
//反射方式调用
public static void test02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class c1 = Class.forName("cn.morant.Reflection.Demo");
Demo demo = (Demo) c1.newInstance();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
//获取10亿次对象的名字
getName.invoke(demo,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式调用花费了"+(endTime-startTime)+"ms");
}
- 关闭安全监测反射方式调用对象的方法
//关闭安全检测的反射方式调用
public static void test03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class c1 = Class.forName("cn.morant.Reflection.Demo");
Demo demo = (Demo) c1.newInstance();
Method getName = c1.getDeclaredMethod("getName", null);
//关闭安全检测
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
//获取10亿次对象的名字
getName.invoke(demo,null);
}
long endTime = System.currentTimeMillis();
System.out.println("关闭安全检测后的反射方式调用花费了"+(endTime-startTime)+"ms");
}
结果
9、获取注解信息
准备:
@Tablemorant("db_user")
class user1{
@Fieldmorant(colname = "名字",type = "String",length = 10)
private String name;
@Fieldmorant(colname = "学号",type = "String",length = 10)
private int id;
public user1() {
}
public user1(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "user1{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
//自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablemorant{
String value();
}
//自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldmorant{
String colname();
String type();
int length();
}
测试:
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class c1 = user1.class;
//获得指定注解的value值
Tablemorant annotation = (Tablemorant) c1.getAnnotation(Tablemorant.class);
System.out.println(annotation.value());
Field name = c1.getDeclaredField("name");
Fieldmorant annotation1 = name.getAnnotation(Fieldmorant.class);
System.out.println(annotation1.colname());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
注意:部分课件图片来自狂神说B站视频!