一、反射
1.1类加载
1.1.1类在内存中的生命周期:加载→使用→卸载
1.1.2类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化,如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载。
类的加载又分为三个阶段:
(1)加载:load
就是指将类型的class字节码数据读入内存
(2)连接:link
①验证:校验合法性等
②准备:准备对应的内存(方法区),创建Class对象,为类变量赋默认值,为静态常量赋初始值。
③解析:把字节码中的符号引用替换为对应的直接地址引用
(3)初始化:initialize(类初始化)即执行类初始化方法,会给类的静态变量赋初始值
1.2java.lang.Class类
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
1.2.1获取Class对象的三种方式
- 类型名.class
要求编译期间已知类型 - 对象.getClass
获取对象的运行时类型 - Class.forName(类的权限定名)
可以获取编译期间未知的类型
//第一种方式获取Class: 类型名.class
Class clazz1 = Person.class;
//第二种方式获取Class: 对象.getClass()
Person person = new Person();
Class clazz2 = person.getClass();
//第三种方式获取Class: Class.forName("类的全限定名")
Class clazz3 = Class.forName("com.yxm.Person");
1.3反射的概念
反射是一种机制/功能,利用该机制/功能可以在程序运行过程中对类进行解剖并操作类中的构造方法,成员方法,成员属性。
可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)
1.4反射的应用
1.4.1 通过反射创建任意引用类型对象的两种方式
1、直接通过Class对象来实例化(要求必须有无参构造)
2、通过获取构造器对象来进行实例化
1.4.1.1方式一:
(1)获取该类型的Class对象(2)创建对象
@Test
public void test2()throws Exception{
Class<?> clazz = Class.forName("com.yxm.test.Student");
Object stu = clazz.newInstance();
System.out.println(stu);
}
@Test
public void test1() throws Exception{
Class clazz=Person.class();
Person person = (Person) clazz.newInstance();
System.out.println(person);
}
1.4.1.1方式二:
(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象
public static void main(String[] args) throws Exception {
//获取Person类的Class对象
Class clazz = Person.class;
//使用第二种方式创建Person类的对象
//获取无参的构造函数
//Constructor constructor = clazz.getDeclaredConstructor();
Constructor constructor = clazz.getDeclaredConstructor(int.class,String.class, String.class);
//使用构造函数创建对象
Person person = (Person) constructor.newInstance(40,"奥巴马","召唤师峡谷");
System.out.println(person);
}
1.4.2 操作任意类型的属性
(1)获取该类型的Class对象
Class clazz = Class.forName("com.yxm.bean.User");
(2)获取属性对象
Field field = clazz.getDeclaredField("username");
(3)设置属性可访问
field.setAccessible(true);
(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象
Object obj = clazz.newInstance();
(4)设置属性值
field.set(obj,"chai");
(5)获取属性值
Object value = field.get(obj);
1.4.2 调用任意类型的方法
(1)获取该类型的Class对象
Class clazz = Class.forName("com.yxm.service.UserService");
(2)获取方法对象
Method method = clazz.getDeclaredMethod("login",String.class,String.class);
(3)创建实例对象
Object obj = clazz.newInstance();
(4)调用方法
Object result = method.invoke(obj,"chai","123);
二、注解(Annotation)
1.1注解概述
注解英文是Annotation,是一种代码级别的说明,和类 接口平级关系。相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上定义。
1.2JDK提供的三个基本的注解
-
@Override
:描述方法的重写. -
@SuppressWarnings
:压制警告. -
@Deprecated
:标记过时
1.3自定义注解
自定义注解语法: @interface 注解名{}
/**
* 定义了注解
*
*/
public @interface Annotation01 {
}
1.4注解属性
1.4.1注解属性的作用
注解属性可以让注解具备携带存储数据的功能
1.4.2注解属性的类型
- 基本类型
- String
- 枚举类型
- 注解类型
- Class类型
- 以上类型的一维数组类型
注意:
一旦注解有属性了,使用注解的时候,属性必须有值
/**
*注解的属性; 格式和接口的方法很类似
* 1.基本类型
2.String
3.枚举类型
4.注解类型
5.Class类型
6.以上类型的一维数组类型
*/
public @interface Annotation02 {
int a();//基本类型
String b();//String
Color c();//枚举类型
Annotation01 d();//注解类型
Class e();//Class类型
String[] f();//一维数组类型
}
1.5元注解
1.5.1元注解的作用
元注解是使用在自定义的注解上,为自定义的注解提供支持的
1.5.2常用的元注解
@Target
:定义该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType
的枚举值
METHOD
:方法
TYPE
:类 接口
FIELD
:字段
CONSTRUCTOR
:构造方法声明
@Retention
:定义该注解保留到那个代码阶段, 值为:RetentionPolicy
类型,默认只在源码阶段保留
SOURCE
:只在源码上保留(默认)
CLASS
:在源码和字节码上保留
RUNTIME
:在所有的阶段都保留
java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME)
1.6提取注解
java.lang.reflect.AnnotatedElement
-
T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。
-
boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的注解有没有。
Class、Method、Field、Constructor等实现了AnnotatedElement接口.
-
Annotation[] getAnnotations():得到所有的注解,包含从父类继承下来的。
-
Annotation[] getDeclaredAnnotations():得到自己身上的注解。
public @interface Annotation01(){
}
@Annotation01
class Demo01(){
@Annotation01
public void fun01(){
}
public void fun02(){
}
}
//1.获得Demo01字节码对象
Class clazz = Demo01.class;
//2. 获得Demo01上面的注解对象
Annotation01 annotation01 = clazz.getAnnotation(Annotation01.class);
//3.反射获得fun01()方法对象
Method method = clazz.getMethod("fun01");
//4.判断fun01()方法上面是否有@Annotation01注解
boolean flag = method.isAnnotationPresent(Annotation01.class);
三、枚举
1.1枚举概述
枚举是 Java 中一种特殊的类,它可以定义固定数量的枚举实例,例如: 性别、交通信号灯、季节等等
一个方法接收的参数是固定范围之内的时候,那么即可使用枚举类型
1.2格式
enum 枚举名 {
}
1.3枚举应用
1.定义枚举,男和女
enum Gender {
MALE, FEMALE; // 男,女
}
2.定义Person类,性别属性用Gender枚举类型
public class Person {
private String name;
private Gender gender;
public Person() {
}
public Person(String name, Gender gender) {
this.name = name;
this.gender = gender;
}
// 省略get/set/toString方法
}
3.使用只能是传入枚举中的固定值
public class Demo02 {
public static void main(String[] args) {
Person p1 = new Person("张三", Gender.MALE);
Person p2 = new Person("张三", Gender.FEMALE);
Person p3 = new Person("张三", "abc");
}
}
1.4枚举中添加成员变量和成员方法
枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等。
public enum Gender {
MALE("male"), FEMALE("female");
public String tag;
Sex(String tag) {
this.tag = tag;
}
public void showTag() {
System.out.println("它的性别标志是: " + tag);
}
}
public class Demo03 {
public static void main(String[] args) {
Person p1 = new Person("张三", Sex.BOY);
Person p2 = new Person("张三", Sex.GIRL);
Sex.BOY.showTag();
Sex.GIRL.showTag();
}
}