注解和反射
注解和反射
在Java的学习中,我们会对代码附有条条框框的要求,注解为我们提供了这么一条渠道
在Java的学习中,我们有时要突破Java本身的规矩,反射的暴力解法也为我们提供了其他的可能性
注解入门
首先我们先讲解以下注解是什么?
- 注解(Annotation)是JDK5开始引用的新技术
- Annotation的作用:
- 不是程序本身,而是对程序做出解释
- 但可以被程序所读取,不同于解释
- Annotation格式:
- 以“@注解名()”在代码中存在,可以添加参数值
- Annotation的使用场景
- 在package,class,method,field上都可以使用,相当于加入了额外的辅助信息
下面给出代码示例:
//什么是注解
public class Demo1 extends Object{
//@Override这个就是注解,帮助程序注释,当下列程序出现错误时,它会有检错作用
@Override
public String toString() {
return super.toString();
}
}
内置注解
下面我们介绍三种内置注解:
- @Override:定义在java.lang.Override中,此注释只适用于修辞手法,表示一个方法打算重写超类中的另一个方法声明
- @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞手法,属性,类,表示不鼓励程序员使用这样的元素,通常因为它很危险或者存在更好的选择
- @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
- 我们需要添加参数使用:
- @SuppressWarnings("all")
- @SuppressWarnings("unchecked")
下面给出代码示例:
public class Demo2 extends Object{
//@Override:重写的注解
@Override
public String toString() {
return super.toString();
}
//@Deprecated:平时不推荐使用,但可以使用,或者应该有更好的方法(main使用时会出现——)
@Deprecated
public static void run(){
}
// @SuppressWarnings():抑制编译时的警告信息,()中需要有参数,该警告可作用于方法也可以作用于类
@SuppressWarnings("all")
public static void start(){
int i = 0;
}
}
元注解
元注解的作用:负责注解其他注解
Java提供了四种元注解来注解其他Annotation
下面我为大家同列出来:
- @Target:用于描述注解的适用范围。“ElementType.METHOD”,“ElementType.TYPE”,“ElementType.FILE”
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。SOURCE<CLASS<RUNTIME
- @Document:表示该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的注解
下面给出示例代码:
package Demo1;
import java.lang.annotation.*;
public class Demo3 {
}
//首先我们定义一个注解
//@Target():用于描述注解的适用范围,()里面要有参数“ElementType.METHOD”,“ElementType.TYPE”,“ElementType.FILE”等范围参数
@Target({ElementType.METHOD,ElementType.TYPE})
//表示需要在什么级别保存该注解信息:SOURCE<CLASS<RUNTIME
@Retention(RetentionPolicy.RUNTIME)
//说明该注解将被包含在javadoc中
@Documented
//说明子类可以继承父类的注解
@Inherited
//下面是注解的标准写法
@interface MyAnnotation{
}
自定义注解
使用@interface来自定义注解,自动继承了java.lang.annotation.Annotation接口
分析:
- @interface用来声明一个注解,格式:public @interface 注解名
- 其中每个方法实际上都是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
下面给出示例代码:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Demo4 {
//我们在方法run前使用注解MyAnnotation1
////若变量具有默认值,可以省略不写;也可以重写更改
@MyAnnotation1(name = "杜昭锦")
public void run(){
}
//当注解只有一个值,且定义为value可以直接书写
@MyAnnotation2("快乐")
public void start(){
}
}
//设置一个注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
//注解可以定义变量,变量必须在使用注解中写出
//若变量具有默认值,可以省略不写
//变量书写格式:变量类型 变量名 () default 默认值
String name() default "侯佳磊";
int age() default 18;
String[] behave() default {"wife","son"};
}
//设置一个注解
@interface MyAnnotation2{
//当只有一个参数时,推荐使用value,在调用时,可以直接书写值,不用写value = “...”
String value();
}
静态语言VS动态语言
动态语言:
- 动态语言是一类在运行时可以改变其结构的语言:例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点来说就是运行时代码可以根据某些条件改变自身结构
- 主要动态语言包括:C#,JavaScript,PHP,Python
静态语言:
- 与动态语言相反,运行时结构不可变的语言;
- 主要静态语言包括:Java,C,C++
- 但Java虽然不是动态语言,但可以通过反射形成“准动态语言”
反射
Java反射机制:
-
是指在运行时去获得一个类的变量和方法信息。然后通过获得到的信息来创建对象,调用方法的一种机制。
-
由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就确认完成确认,在运行时仍旧可以扩展
反射获得Class类的对象
我们想要通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是Class类对象
我们提供三种方法:
- 使用类的class属性去获得该类对应的Class对象
- 调用该类的对象的getclass()方法,返回该对象所属类的Class对象
- 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,其参数是类对象的全路径
下面给出示例代码:
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//注意:一个类无论如何获取其Class都是一样的
//通过类的class方法获取Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
//通过类对象的getclass方法获取Class对象
Student student = new Student("胡桃",18);
Class<? extends Student> c2 = student.getClass();
System.out.println(c2);
//通过Class固定方法forName(String className)获取Class对象
Class<?> c3 = Class.forName("Demo2.Student");
System.out.println(c3);
}
}
public class Student {
String name;
int age;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反射获得构造方法并使用
Class类中获得构造方法的方法有以下四种:
- Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
- Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
- Constructor getConstructor(Class...parameterTypes):返回单个公共构造方法对象
- Constructor getDeclaredConstructor(Class...parameterTypes):返回单个构造方法对象
Constructor类用于创造对象的方法:
- T newInstance(Object...initargs):根据指定的构造方法创建对象
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//首先我们获得Student的Class对象
Class<?> c = Class.forName("Demo2.Student");
//第一种方法获得构造方法(c.getConstructors())
//这种方法获得public的构造方法
Constructor<?>[] cons1 = c.getConstructors();
for (Constructor<?> con : cons1){
System.out.println(con);
}
System.out.println("--------");
//第二种方法获得构造方法(c.getDeclaredConstructors())
//这种方法获得public和private所有构造方法
Constructor<?>[] cons2 = c.getDeclaredConstructors();
for (Constructor<?> con : cons2){
System.out.println(con);
}
System.out.println("--------");
//第三中方法获得构造方法( c.getConstructor()和c.getDeclaredConstructor())
//这种方法采用获得单个方法来获得方法
Constructor<?> con1 = c.getConstructor();
Constructor<?> con2 = c.getDeclaredConstructor();
//然后我们采用con的方法创造对象(这里采用的无参构造方法)
Object obj = con1.newInstance();
System.out.println(obj);
}
}
public class Student {
String name;
int age;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反射获得构造方法并使用练习
练习1:
- 通过反射实现下列操作:
- Student s = new Student("侯佳磊", 18);
- System.out.println(s);
注意:基本数据类型也可以通过.class获得对应的Class类型
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//首先创造Student的Class反射
Class<?> c = Class.forName("Demo2.Student");
//然后我们采取方法获得构造方法( c.getConstructor)
//这里我们给参数时也要给出对应类型的class参数
Constructor<?> con = c.getConstructor(String.class, int.class);
//然后我们开始构造
Object obj = con.newInstance("侯佳磊", 18);
//最后输出查看
System.out.println(obj);
}
}
public class Student {
String name;
int age;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
练习2:
- 通过反射实现如下操作
- Students s = new Student("侯佳磊")
- System.out.println(s)
注意:public void setAccessible(boolean flag):值为true时,取消访问检查
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//首先创建反射
Class<?> c = Class.forName("Demo2.Student");
//这里采用private方法,我们需要采用c.getDeclaredConstructor(String.class)
Constructor<?> con = c.getDeclaredConstructor(String.class);
//开始构造
//正常情况下我们不可以直接私用方法
//但反射给我们提供了一种方法con.setAccessible(),当参数为true时可以取消访问检查,被称为暴力反射
con.setAccessible(true);
Object obj = con.newInstance("侯佳磊");
//输出查看
System.out.println(obj);
}
}
public class Student {
String name;
int age;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反射获得成员变量并使用
Class类中获得成员变量方法的方法有以下四种:
- Field<?>[] getFields():返回所有公共变量对象的数组
- Field<?>[] getDeclaredFields():返回所有变量对象的数组
- Field<?> getField(String name):返回单个公共变量对象
- Field<?> getDeclaredField(String name):返回单个变量对象
Field类中用于给成员变量赋值的方法:
- void set(Object obj,Object value):给obj对象的成员变量赋值为value
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//首先我们创建反射
Class<?> c = Class.forName("Demo3.Student");
//获得成员变量的方法和获得构造方法的格式基本一致,分为四种:
//c.getFields():获得所有public变量
Field[] conFields1 = c.getFields();
//c.getDeclaredFields():获得所有public,private变量
Field[] conFields2 = c.getDeclaredFields();
//c.getField():获得单个public变量
Field nameField = c.getField("name");
//c.getDeclaredField();
Field addressField = c.getDeclaredField("address");
//然后我们讲解如何赋值
//首先我们需要一个由反射创建的对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//然后我们用Field的方法来赋值给反射对象
addressField.set(obj,"西安");
//输出检验
System.out.println(obj);
}
}
public class Student {
public String name;
private int age;
public String address;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
反射获得成员变量并使用练习
练习:
- 通过反射实现如下操作
- Student s =new Student()
- s.name = "侯佳磊"
- s.age = 30
- s.address = "西安"
- System.out.println(s)
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException, InstantiationException, IllegalAccessException {
//老规矩创造反射
Class<?> c = Class.forName("Demo3.Student");
//创造无参对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//依次创造变量对象并赋值
//注意:private的变量不能直接赋值,我们需要采用暴力方法
Field nameField = c.getField("name");
nameField.set(obj,"侯佳磊");
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj,18);
Field address = c.getField("address");
address.set(obj,"西安");
//最后输出查看
System.out.println(obj);
}
}
public class Student {
public String name;
private int age;
public String address;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
反射获得成员方法并使用
Class类中获得成员方法的方法有以下四种:
- Method[] getMethods():返回所有公共成员方法的数组
- Method[] getDeclaredMethods():返回所有成员方法的数组
- Method getMethod(String name,Class<?>...parameterTypes):返回单个公共成员方法对象
- Method getDeclaredMethod(String name,Class<?>...parameterTypes):返回单个成员方法对象
Method类中用于调用成员方法的方法:
- Object invoke(Object obj,Object...args):调用obj对象的成员方法,参数是args,返回值类型是Object
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//首先创建反射
Class<?> c = Class.forName("Demo4.Student");
//然后我们创造无参对象备用
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//反射获得方法的操作和之前操作类似,分为四种
//c.getMethods():获得所有public方法。注意这里包括父类的public方法
Method[] methods1 = c.getMethods();
//c.getDeclaredMethods():获得所有类型的方法,注意这里不包括父类的方法
Method[] Methods2 = c.getDeclaredMethods();
//c.getMethod(String name,Class<?> parameter Types):获得单个公共成员方法对象
Method method1 = c.getMethod("method1");
Method method2 = c.getMethod("setName", String.class);
//c.getDeclaredMethod(String name,Class<?> parameter Types):获得单个成员方法对象
Method method3 = c.getDeclaredMethod("method2");
//下面我们给出Method的固定方法使用格式
//Object invoke(Object obj,Object...args):调用obj对象的Object方法,以args为参数
method1.invoke(obj);
method2.invoke(obj,"侯佳磊");
//下面输出结果测试
System.out.println(obj);
}
}
public class Student {
public String name;
private int age;
public String address;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
public void method1(){
System.out.println("method1");
}
public void method2(){
System.out.println("method2");
}
public void method3(String name){
System.out.println("method3:" + name);
}
public String method4(String name,int age){
return name + "," + age;
}
private void method5(){
System.out.println("method5");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
反射获得成员方法并使用练习
练习:
- 通过反射实现如下操作
- Student s = new Student()
- s.method1()
- s.method3()
- s.method4()
- s.method5()
下面给出示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//创造Class对象
Class<?> c = Class.forName("Demo4.Student");
//创造无参对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//首先调用method1
Method method1 = c.getMethod("method1");
method1.invoke(obj);
//然后调用method3
Method method3 = c.getMethod("method3", String.class);
method3.invoke(obj,"侯佳磊");
//然后调用method4
Method method4 = c.getMethod("method4", String.class, int.class);
Object s = method4.invoke(obj, "侯佳磊", 18);
System.out.println(s);
//最后调用method5
Method method5 = c.getDeclaredMethod("method5");
method5.setAccessible(true);
method5.invoke(obj);
}
}
public class Student {
public String name;
private int age;
public String address;
public Student(){
}
private Student(String name){
this.name = name;
}
private Student(int age){
this.age = age;
}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
public void method1(){
System.out.println("method1");
}
public void method2(){
System.out.println("method2");
}
public void method3(String name){
System.out.println("method3:" + name);
}
public String method4(String name,int age){
return name + "," + age;
}
private void method5(){
System.out.println("method5");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
ArrayList反射练习
需求:
- 目前我们有一个ArrayList
集合 - 现在我希望在这个集合中假如字符串数据
下面给出示例代码:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
//我们创造一个ArrayList<Integer>集合,向里面加入String类型的数据
public class Demo1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//我们创造一个ArrayList<Integer>集合
ArrayList<Integer> arr = new ArrayList<>();
//我们采用add直接添加方法是不能实现的
//所以我们需要直接创建class对象
Class<? extends ArrayList> c = arr.getClass();
//我们获取ArrayList<Integer>的方法add
Method method = c.getMethod("add",Object.class);
method.invoke(arr,"Hello");
method.invoke(arr,"World");
//最后输出检验
System.out.println(arr);
}
}
结束语
这次只是简单对反射进行介绍,后续可能会做出相应补充内容