day21【反射、注解】
反馈和复习
1.这两天学的不好,没有信心了
2.写代码出现一些未知异常,怎么办??? 去百度 去google 请教别人
BIO: 同步阻塞的IO, 调用某个方法时,该方法没有完成程序不能继续向下执行
NIO: 同步非阻塞的IO,调用某个方法时,无论该方法是否完成,程序可以继续向下执行,后期需要自己写代码判断
AIO: 异步非阻塞的IO,调用某个方法时,无论该方法是否完成,程序可以继续向下执行,后期会通过方法回调机制通知
今日内容
今天的内容是重点!!!
1.反射【反射是以后基本上所有框架的底层,这是重点】
2.注解【也是给我们以后的框架使用,重点,今天我们只介绍注解的语法】
第一章 反射
1.类加载器【了解】
-
类的加载【重点】
比如,现在我们定义了一个类,Student,我们使用它时候才会加载到内存 JVM会为这个Student.class字节码文件创建一个对象,该对象我们称为Class对象(字节码文件对象)
-
类的加载时机
1. 创建类的实例。 2. 调用类的静态变量,或者为静态变量赋值。 3. 调用类的静态方法。 4. 使用某个类的子类. 5. 直接使用java.exe命令来运行某个主类 6. 使用反射强制加载某个类,并生成Class对象
-
类加载器的作用和分类
我们用到类都是由类加载器负责加载到内存的!!! 类加载器有三种: 启动类加载器(引导类加载器,Bootstrap ClassLoader),加载%JAVA_HOME%\bin目录下的所有类 扩展类加载器(Extension ClassLoader),加载一些%JAVA_HOME%\jre\lib\ext扩展类 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的类加载器
-
双亲委派机制
当某个类加载器需要加载一个类时,它并不是直接去加载,而把加载的需要交给父级类加载器,最终请求会达到到达启动类加载器,启动类加载判断是否可以加载该类,如果加载不了再交给扩展类加载器,扩展类加载判断是否可以加载,如果加载不了,再交给应用程序类加载器. 双亲委派机制主要作用就是让一个类只会被加载一次!!!
2.什么是反射
反射就是在程序运行时获取一个类的Class对象(字节码文件对象),从解剖它,取出其中的各种成员,使用这些成员
3.反射在实际开发中的应用
a.开发"IDEA"等之类的IDE
b.学习各种框架的设计和底层原理: Spring,SpringMVC,Mybatis,...
4.反射中万物皆对象的概念
Class对象 --> 某个字节码文件
Field对象 --> 某个成员变量
Method对象 --> 某个成员方法
Constructor对象 --> 某个构造方法
newInstance --> 创建对象
invoke --> 调用/执行
书籍:<<深入理解JVM虚拟机>> 很有意思
体验一下反射的语法(和正常语法是方过来写的)
创建对象:
正常语法: new 构造方法(参数);
反射语法: 构造方法对象.newInstance(参数);
调用方法:
正常语法: 对象名.成员方法名(参数);
反射语法: 成员方法对象.invoke(对象名,参数);
5.反射的第一步获取字节码文件对象(Class对象)【重点】
Java定义了三种获取Class对象的方式
public class GetClassDemo {
public static void main(String[] args) throws ClassNotFoundException {
//获取一个类的Class对象的三种方式
//1.通过类的一个静态成员 class
Class clazz1 = Dog.class;
System.out.println(clazz1);
//2.通过该类的一个对象,获取该类的Class对象
Dog dd = new Dog();
Class clazz2 = dd.getClass();
System.out.println(clazz2);
//3.通过反射强制加载该类,并获取该类的Class对象
Class clazz3 = Class.forName("com.itheima.demo02_GetClass.Dog");
System.out.println(clazz3);
//注意:以上是三种获取Dog类Class对象的方式,并不是获取三个Class对象,实际上他们获取的是同一个Class对象
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
System.out.println(clazz2 == clazz3);
}
}
输出结果:
class com.itheima.demo02_GetClass.Dog
class com.itheima.demo02_GetClass.Dog
class com.itheima.demo02_GetClass.Dog
true
true
true
6.Class对象中的三个方法
public String getName();
public String getSimple();
public Object newInstance();
public class ClassMethodDemo {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class clazz = Dog.class;
//Class对象中三个方法
//1.获取全限定类名
System.out.println(clazz.getName());
//2.获取类名
System.out.println(clazz.getSimpleName());
//3.创建Class对象所代表的那个类的对象,底层实际上使用Dog的无参构造
Dog dog = (Dog) clazz.newInstance();
System.out.println(dog);
}
}
输出结果:
com.itheima.demo03_ClassMathod.Dog
Dog
Dog{age=0, name='null'}
7.通过反射获取构造方法&&使用构造方法创建对象【重点】
-
反射获取构造方法
public Constructor getConstructor(Class... parameterTypes); 获取单个"public"构造 public Constructor getDeclaredConstructor(Class... parameterTypes);获取单个"任意修饰"构造 public Constructor[] getConstructors(); 获取所有"public"构造 public Constructor[] getDeclaredConstructors();获取所有"任意修饰"构造 public class GetConstructorDemo { public static void main(String[] args) throws NoSuchMethodException { //1.反射第一步,先获取Class对象 Dog dd = new Dog(); Class clazz = dd.getClass(); //2.获取单个"public"的构造方法 Constructor con1 = clazz.getConstructor(); System.out.println(con1); // Constructor con2 = clazz.getConstructor(int.class, String.class); // System.out.println(con2); //3.获取单个"任意修饰符"的构造方法 Constructor con3 = clazz.getDeclaredConstructor(int.class, String.class); System.out.println(con3); System.out.println("========================"); //4.获取所有"public"构造 Constructor[] cons = clazz.getConstructors(); System.out.println(cons.length); for (Constructor con : cons) { System.out.println(con); } //5.获取所有"任意修饰符"构造 System.out.println("========================"); Constructor[] conss = clazz.getDeclaredConstructors(); System.out.println(conss.length); for (Constructor con : conss) { System.out.println(con); } } }
-
使用构造方法创建对象
语法: 构造方法对象.newInstance(参数); @Test public void test02() throws Exception { //使用构造 //1.反射第一步,先获取Class对象 Dog dd = new Dog(); Class clazz = dd.getClass(); //2.获取单个"public"的构造方法 Constructor con1 = clazz.getConstructor(); System.out.println(con1); Constructor con2 = clazz.getConstructor(int.class,String.class); System.out.println(con2); //3.使用构造创建对象 Dog dog1 = (Dog)con1.newInstance(); System.out.println(dog1); Dog dog2 = (Dog)con2.newInstance(10,"旺财"); System.out.println(dog2); } 输出结果: public com.itheima.demo04_GetConstructor.Dog() public com.itheima.demo04_GetConstructor.Dog(int,java.lang.String) Dog{age=0, name='null'} Dog{age=10, name='旺财'}
-
如果是私有构造怎么办
私有构造必须先设置暴力权限,然后才能正常使用,否则抛出IllegalAccessException异常 @Test public void test02() throws Exception { //使用构造 //1.反射第一步,先获取Class对象 Dog dd = new Dog(); Class clazz = dd.getClass(); //2.获取单个"public"的构造方法 Constructor con1 = clazz.getConstructor(); System.out.println(con1); Constructor con2 = clazz.getDeclaredConstructor(int.class,String.class); System.out.println(con2); //3.使用构造创建对象 Dog dog1 = (Dog)con1.newInstance(); System.out.println(dog1); //私有构造,不能直接使用,因为没有权限 //设置暴力访问权限 con2.setAccessible(true); Dog dog2 = (Dog)con2.newInstance(10,"旺财"); System.out.println(dog2); } 输出结果: public com.itheima.demo04_GetConstructor.Dog() private com.itheima.demo04_GetConstructor.Dog(int,java.lang.String) Dog{age=0, name='null'} Dog{age=10, name='旺财'}
8.通过反射获取成员方法&&调用成员方法【重点】
-
反射获取成员方法
public Method getMethod(String name,Class...args);获取"public"方法 public Method getDeclaredMethod(String name,Class...args);获取"任意修饰"方法 public Method[] getMethods(); 获取所有"public"方法,包括父类继承的 public Method[] getDeclaredMethods(); 获取所有"任意修饰"方法,不包含父类继承的 @Test public void test01() throws NoSuchMethodException { //1.获取Class对象 Dog dd = new Dog(); Class clazz = dd.getClass(); //2.获取单个"public"成员方法 Method eat1 = clazz.getMethod("eat"); System.out.println(eat1); Method eat2 = clazz.getMethod("eat", String.class, String.class); System.out.println(eat2); //3.获取单个"任意修饰"成员方法 Method eat3 = clazz.getDeclaredMethod("eat", String.class); System.out.println(eat3); System.out.println("====================="); //4.获取所有的"public"成员方法,包括父类继承的 Method[] methods = clazz.getMethods(); System.out.println(methods.length); for (Method method : methods) { System.out.println(method); } System.out.println("====================="); //5.获取所有的"任意修饰"的成员方法,但是不包含父类继承的 Method[] methodss = clazz.getDeclaredMethods(); System.out.println(methodss.length); for (Method method : methodss) { System.out.println(method); } }
-
调用成员方法
语法格式: 成员方法对象.invoke(对象名,参数); @Test public void test02() throws Exception { //调用成员方法 //1.获取Class对象 Dog dd = new Dog(); Class clazz = dd.getClass(); //2.获取单个"public"成员方法 Method eat1 = clazz.getMethod("eat"); System.out.println(eat1); Method eat2 = clazz.getMethod("eat", String.class, String.class); System.out.println(eat2); //3.使用成员方法 eat1.invoke(dd); eat2.invoke(dd,"shi","niao"); } 输出结果: public void com.itheima.demo05_GetMethod.Dog.eat() public void com.itheima.demo05_GetMethod.Dog.eat(java.lang.String,java.lang.String) 狗爱吃shi... 狗爱吃shi,爱喝niao
-
如果是私有的成员方法怎么调用呢?
私有成员方法不能直接调用,必须先设置暴力访问权限,否则抛出IllegalAccessException异常 @Test public void test02() throws Exception { //调用成员方法 //1.获取Class对象 Dog dd = new Dog(); Class clazz = dd.getClass(); //2.获取单个"public"成员方法 Method eat1 = clazz.getMethod("eat"); System.out.println(eat1); Method eat2 = clazz.getMethod("eat", String.class, String.class); System.out.println(eat2); Method eat3 = clazz.getDeclaredMethod("eat", String.class); //3.使用成员方法 eat1.invoke(dd); eat2.invoke(dd,"shi","niao"); //私有方法不能直接调用,必须先设置暴力访问权限 eat3.setAccessible(true); eat3.invoke(dd,"shi"); } 输出结果 public void com.itheima.demo05_GetMethod.Dog.eat() public void com.itheima.demo05_GetMethod.Dog.eat(java.lang.String,java.lang.String) 狗爱吃shi... 狗爱吃shi,爱喝niao 狗爱吃shi
9.通过反射获取成员属性【了解,自学】
第二章 注解
1.什么是注解
a.注解是JDK1.5的新特性(注解,增强for,泛型,可变参数)
b.注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息
c.标记(注解),可以用在各种地方(包,类,构造方法,普通方法,成员变量,局部变量,...)
d.注解,主要是给编译器或者JVM看的,用于完成某些特定的功能
2.注解的三个作用
a.给程序带入一些参数
b.编译检查
c.给框架使用,作为框架的配置文件
3.常见的注解介绍
@author:用来标识作者名
@version:用于标识对象的版本号
@Override: 用于标识该方法是重写的
@deprecated: 用于标识过期的API
@Test: 用于单元测试的注解
4.自定义注解(Annotation)【重点】
注意:我们今天自学语法,不学作用
自定义类: public class 类名
自定义接口: public interface 接口
自定义枚举: public enum 枚举名
自定义注解: public @interface 注解名
格式:
public @interface 注解名{
}
5.给自定义注解添加属性【重点】
格式:
public @interface 注解名{
//注解内部只有属性,没有别的!!
数据类型 属性名();
数据类型 属性名() [default 默认值];
}
注解中并不是所有数据类型都可以的!!!
只能是以下三大类型:
a.八大基本类型(byte,short,char,int,long,float,double,boolean)
b.String,Class,注解类型,枚举类
c.以上12具体数据类型的数组
6.自定义注解练习【重点】
需求:
定义一个注解:Book
包含属性:String value() 书名
包含属性:double price() 价格,默认值为 100
包含属性:String[] authors() 多位作者
/**
* 自定义的注解
*/
public @interface Book {
//属性
String value(); //书名
double price() default 100.0; //价格
String[] authors();
}
7.使用注解时的注意事项【重点】
使用格式:
@注解名(属性名=属性值,属性名=属性值)
@Book(value = "三国演义",price = 150.0,authors = {"罗贯中","杨洋"})
public class Demo {
@Book(value = "红楼梦",authors = {"曹雪芹"})
private int age;
private String name;
public Demo() {
}
@Book(value = "水浒传",authors = "吴承恩")
public Demo(int age, String name) {
this.age = age;
this.name = name;
}
public void show(int age){
String name = "张三";
System.out.println(age);
}
}
注意:
a.使用注解时保证注解的每个属性都必须有值(有默认值我们可以不再赋值,没有默认值我们必须赋值)
b.如果是数组需要使用{}把值括起来,如果数组的值只有一个,那么大括号可以省略
8.自定义注解中的特殊属性名value【重点】
a.如果注解中"只有一个属性",并且名字叫做"value",那么使用时可以直接写属性的值,省略属性名
b.如果注解中有value之外的其他属性,那么其他属性都有默认值,
且使用注解时只给value赋值,那么直接写属性的值,省略属性名.
/**
* 特殊属性Value
*/
public @interface Book {
//属性
String value(); //书名
double price() default 100.0;
}
public class Demo {
private int age;
private String name;
public Demo() {
}
@Book("三国演义")
public Demo(int age, String name) {
this.age = age;
this.name = name;
}
public void show(int age){
String name = "张三";
System.out.println(age);
}
}
9.注解的注解–元注解(了解即可)
a.元注解是Java官方提供的注解
b.元注解用来修饰我们定义的注解(注解的注解)
-
两个元注解
元注解常见的有两个: @Target 元注解 作用:用来标识注解使用的位置,如果没有标识,那么我们的注解在各种地方都可以使用 取值:必须使用ElementType枚举下的值: TYPE,类,接口 FIELD, 成员变量 METHOD, 成员方法 PARAMETER, 方法参数 CONSTRUCTOR, 构造方法 LOCAL_VARIABLE, 局部变量 //元注解@Target //@Target(ElementType.CONSTRUCTOR) //@Target(ElementType.FIELD) //@Target(ElementType.LOCAL_VARIABLE) //使用元注解修饰我们定义的注解 @Target({ElementType.FIELD,ElementType.METHOD}) public @interface MyAnno { } //使用我们定义的注解 @MyAnno public class Demo { @MyAnno private int age; @MyAnno private String name; @MyAnno public Demo() { } @MyAnno public Demo(@MyAnno int age,@MyAnno String name) { this.age = age; this.name = name; } @MyAnno public void show(@MyAnno int age){ @MyAnno String name = "张三"; System.out.println(age); } } @Retention 元注解 作用:用来标识我们注解的生命周期(有效范围) 取值:必须是RetentionPolicy枚举中的下面三个值之一 SOURCE 表示我们的注解只在源码阶段存在,编译成字节码文件后删除 CLASS 表示我们的注解在源码阶段和字节码阶段存在,加载到内存后删除 RUNTIME 表示我们的注解在源码阶段,字节码阶段,运行时都存在(永远不删除)
10.注解的解析(理解)
-
什么是注解解析
通过写代码获取出来某个注解中的那些属性值
-
注解解析的步骤
a.获取注解所在的那个类的Class对象 b.获取注解所在的对象(可能Field,Method,Constructor) c.判断获取的对象中是否有该注解存在 d.如果有我们要的注解,取出我们要的注解即可 e.从注解中取出属性值即可 与之相关的API: Annotation: 注解类,Java中所有的注解的父类(了解) 我们学过的Field,Method,Constructor,Class等类都是实现了AnnotatedElement接口 public boolean isAnnotationPresent(Class annotationClass);判断是否存在某个注解 Annotation getAnnotation(Class annotationClass);获取指定类型的注解
-
注解解析代码实现
a.获取注解所在的那个类的Class对象 b.获取注解所在的对象(可能Field,Method,Constructor) c.判断获取的对象中是否有该注解存在 d.如果有我们要的注解,取出我们要的注解即可 e.从注解中取出属性值即可 @Retention(RetentionPolicy.RUNTIME) public @interface Student { int age(); String name(); String[] boyFriends(); } public class Demo { @Student(age = 18, name = "小花", boyFriends = {"张三", "李四", "王五"}) public void show() { } } public class TestStudent { public static void main(String[] args) throws Exception { // a.获取注解所在的那个类的Class对象 Class clazz = Demo.class; // b.获取注解所在的对象(可能Field,Method,Constructor) Method showMethod = clazz.getMethod("show"); // c.判断获取的对象中是否有该注解存在 if (showMethod.isAnnotationPresent(Student.class)) { System.out.println("有该注解"); // d.如果有我们要的注解,取出我们要的注解即可 Student anno = showMethod.getAnnotation(Student.class); // e.从注解中取出属性值即可 int age = anno.age(); String name = anno.name(); String[] friends = anno.boyFriends(); System.out.println("姓名:"+name); System.out.println("年龄:"+age); System.out.println("男友们:"+ Arrays.toString(friends)); } else { System.out.println("没有该注解"); } } }
11.注解解析案例(理解)
需求:
模拟Junit的@Test注解
/**
* 自定义的注解,用于模拟@Test注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
}
public class Demo {
@MyTest
public void test01(){
System.out.println("11111");
}
@MyTest
public void test02(){
System.out.println("22222");
}
@MyTest
public void test03(){
System.out.println("33333");
}
@MyTest
public void test04(){
System.out.println("44444");
}
}
public class TestMyTestDemo {
public static void main(String[] args) throws Exception {
//当这个main方法执行时,要求Demo类中有@MyTest注解的方法会执行
//没有@MyTest注解的方法不会执行
//思考:步骤
//1.获取@MyTest注解所在的类
Class clazz = Demo.class;
//2.获取所有方法
Method[] methods = clazz.getMethods();
//3.循环
for (Method method : methods) {
//4.判断
if (method.isAnnotationPresent(MyTest.class)) {
//有该注解
method.invoke(new Demo());
}else{
//没有该注解
}
}
}
}
总结
能够通过反射技术获取Class字节码对象。【重点】
Class clazz1 = 类名.class
Class clazz2 = 对象名.getClass();
Class clazz3 = Class.forName("包名.类名");
能够通过反射技术获取构造方法对象,并创建对象。【重点】
获取构造:
public Constructor getConstructor(参数的类型.class,...);//获取单个"public"构造
public Constructor getDeclaredConstructor(参数的类型.class,...);//获取单个"任意修饰"构造
public Constructor[] getConstructors();//获取所有"public"构造【了解】
public Constructor[] getDeclaredConstructors();//获取所有"任意修饰"构造【了解】
使用构造:
构造方法对象.newInstance(实际参数);
如果是私有构造: 必须在使用之前设置暴力权限
构造方法对象.setAccessible(true);
能够通过反射获取成员方法对象,并且调用方法 【重点】
获取方法:
public Method getMethod(String name,参数的类型.class,...);//获取单个"public"方法
public Method getDeclaredMethod(String name,参数的类型.class,...);//获取单个"任意修饰"方法
public Method[] getMethods();//获取所有"public"方法,包含父类继承的【了解】
public Method[] getDeclaredMethods();//获取所有"任意修饰"方法,不包含父类的【了解】
使用方法:
成员方法对象.invoke(对象名,方法的实际参数);
如果该方法是私有的,那么必须在使用之前设置暴力权限
成员方法对象.setAccessible(true);
能够通过反射获取属性对象,并且能够给对象的属性赋值和取值【了解,有兴趣自学】
能够说出注解的作用
a。给程序带入参数 b。编译检查 c。给框架做配置文件
能够自定义注解和使用注解【重点】
自定义注解:
public @interface 注解名{
数据类型 属性名();
数据类型 属性名() default 默认值;
数据类型[] 属性名() default {默认值1,默认值2,...}
}
这里的数据类型,只能是三大类(a.8大基本类型 b.四大引用类型 c.以上12中的数组)
特殊的属性:value(怎么特殊???)
能够说出常用的元注解及其作用 【理解】
@Target
@Retension
能够解析注解并获取注解中的数据 【理解】
写代码把某个注解上的那些属性值获取打印出来
a.获取注解所在的那个类的Class对象
b.获取注解所在的对象(可能Field,Method,Constructor)
c.判断获取的对象中是否有该注解存在
d.如果有我们要的注解,取出我们要的注解即可
e.从注解中取出属性值即可
能够完成注解的MyTest案例 【理解】
本质上也是注解的解析,只是没有获取注解的属性值,而是调用含有该注解的方法