java基础三(注解与反射机制)

一、注解(Annotation)

1.什么是注解?

1、概念
java中注解主要用于标记代码,或结合其他方法,如反射,实现值的传递。另外,注解在框架中也使用较多。
java中有7种自带的注解,包括@Override, @Deprecated, @SuppressWornings 和四种元注解(表示注解的注解)。jdk7后,逐步增加为10种。

按来源分:
1.java自带注解:
@Override
表示打算重写超类中的方法声明。
@Deprecated
表示废弃,这个注释可以修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为他很危险或有更好的选择。
@SuperWarnings
这个注解主要是用来抑制警告信息的。与前俩个注解不同的是我们必须给注解参数才能使用他。
元注解:
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问(即属于前面定义的三种生命周期中的哪一种)。
@Retention
**这个注解用于描述注解的生命周期。
取值RetentionPolicy:
SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在运行时有效(即运行时保留)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
	//参数默认为空
   String value() default "";
}

注:为RUNTIME时可以被反射机制所读取
这个注解用于描述注解的生命周期。
取值RetentionPolicy:
SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在运行时有效(即运行时保留)

在一般情况下我们使用RUNTIME。这样在程序运行时我们也可以通过反射机制来读取到该注解。

@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
使用方法:
这个注解的作用主要是用来描述注解的使用范围,说白了就是我们自己定义的注解可以使用在哪个地方。所修饰范围取值ElementType:
package 包:PACKAGE
类、接口、枚举、Annotation类型:TYPE
类型成员(方法,构造方法,成员变量,枚举值)
CONSTRUCTOR:用于描述构造器。
FIELD:用于描述域。
METHOD:用于描述方法
方法参数和本地变量
LOCAL_VARIABLE:用于描述局部变量。
PARAMETER:用于描述参数

我们以ElementType.METHOD为例。

@Target(ElementType.METHOD)
public @interface TestAnnotation {
	//参数默认为空
    String value() default "";
}

@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何类)
新增注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

2.第三方注解,如Spring全家桶中的注解;

3.自定义注解,使用 @interface 关键字定义。
格式:public @interface 注解名 { 定义体 }
1.使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
2.其中的每一个方法实际上是声明了一个配置参数
3.方法的名称就是参数的名称
4.返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
5.可以通过default来声明参数的默认值
5.如果只有一个参数成员,一般参数名为value
我们在使用注解元素时必须要有值,可以定义默认值,空字符串,0或者-1

public @interface TestAnnotation {
    //参数默认为空
    String value() default "";
}

二、什么是反射:

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
2、反射的原理:(借鉴别人的图)
在这里插入图片描述

3、反射的优缺点:
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,更加容易实现面向对象。
2、缺点:
(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射。
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
4、反射的用途:
1、反编译:.class–>.java
2、通过反射机制访问java对象的属性,方法,构造方法等
3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
5例如,在使用Strut2框架的开发过程中,我们一般会在struts.xml里去配置Action
以及加载数据库驱动的,用到的也是反射。
Class.forName(“com.mysql.jdbc.Driver”); // 动态加载mysql驱动

//struts.xml里去配置Action
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">   
    <result>/shop/shop-index.jsp</result>           
    <result name="error">login.jsp</result>       
</action>

5、反射的基本使用:
1、获得Class:主要有三种方法:
(1)Object–>getClass
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
(3)通过class类的静态方法:forName(String className)(最常用)

/*
* 获得Class的三种主要方法
* 三种方式中,常用第三种,第一种对象都有了还要反射干什么,
* 第二种需要导入类包,依赖太强,不导包就抛编译错误。
* 一般都使用第三种,一个字符串可以传入也可以写在配置文件中等多种方法。
* */
public  class day6 {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种方式获取Class对象
        Stu stu = new Stu("yan", 20);
        Class stuClass = stu.getClass();//获取Class对象
        System.out.println(stuClass.getName());
        //第二种获取class对象
        Class staClass2 = Stu.class;
        System.out.println(stuClass == staClass2);//判断第一种或者第二中获取的
        //第三种获取
        Class stuClass3 = Class.forName("fanshe.Student");
        System.out.println(staClass2 == stuClass3);
    }
}

注意,在运行期间,一个类,只有一个Class对象产生,所以打印结果都是true;

/**
 * 创建实例:通过反射生成对象
 * */
public  class day6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //使用Class对象的newInstance()方法
        Class<?> c = String.class;
        Object str = c.newInstance();
        //通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象
        Class<?> str2 = String.class;
//通过Class对象获取指定的Constructor构造器对象
        Constructor constructor=str2.getConstructor(String.class);
//根据构造器创建实例:
        Object obj = constructor.newInstance("hello");
        System.out.println(str);
    }
}

2、判断是否为某个类的示例:
我们可以借助反射中Class对象的isInstance()方法来判断,他是一个native方法。
public native boolean isInstance(Object obj);
3、创建实例:通过反射来生成对象主要有两种方法:
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。

反射方法的其他使用–通过反射运行配置文件内容:
Student类:
public class Student {
public void show(){
System.out.println(“is show()”);
}
}

/**
 * 创建实例:通过反射生成对象
 * */
public  class day6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //使用Class对象的newInstance()方法
        Class<?> c = String.class;
        Object str = c.newInstance();
        //通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象
        Class<?> str2 = String.class;
//通过Class对象获取指定的Constructor构造器对象
        Constructor constructor=str2.getConstructor(String.class);
//根据构造器创建实例:
        Object obj = constructor.newInstance("hello");
        System.out.println(str);
    }

/**
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 *
 * 1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 * 		3).调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */
public class day6{
    public static void main(String[] args)throws Exception {
        //1获取Class对象
        Class stuClass= Class.forName("yanqing.Student");
        //所有公共的构造方法
        Constructor[] conArray=stuClass.getConstructors();
        for(Constructor c:conArray){
            System.out.println(c);
        }
        //所有的构造方法(包括私有,受保护,默认,公有)
        conArray=stuClass.getDeclaredConstructors();
        for(Constructor c:conArray){
            System.out.println(c);
        }
        //获取公有,无参的构造方法
        Constructor con=stuClass.getConstructor(null);
        //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
        //2>、返回的是描述这个无参构造函数的类对象。
        System.out.println(con);
        //调用构造方法
        Object obj=con.newInstance();
        //调用私有构造方法并调用
        con=stuClass.getDeclaredConstructor(char.class);
        obj=con.newInstance("男");
    }
}
/**
 * 获取成员变量并调用
 * */
public class day6{
    public static void main(String[] args)throws Exception {
       Class stuClass=Class.forName("yanqing.Student");
       //获取所有公有的字段
        Field[] fields=stuClass.getFields();
        for(Field f:fields){
            System.out.println(f);
        }
        //获取所有字段
        fields=stuClass.getDeclaredFields();
        for (Field f:fields){
            System.out.println(f);
        }
        //获取所有公共字段***并调用
        Field f=stuClass.getField("name");
        System.out.println(f);
        Object obj=stuClass.getConstructor().newInstance();
        f.set(obj,"yanqing");
        Student stu=(Student)obj;
        System.out.println("验证姓名:"+stu.name);
        //获取私有字段****并调用
        f=stuClass.getDeclaredField("phoneNum");
        System.out.println(f);
        f.setAccessible(true);
        f.set(obj,"561561616");
        System.out.println("验证电话:"+stu);
    }
}

/**
 * 获取成员方法并调用:
 *
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 * 	 调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
):
 */
public class day6{
    public static void main(String[] args)throws Exception {
        Class stuClass=Class.forName("yanqing.Student");
        //获取所有的公有方法
        Method[] methods=stuClass.getMethods();
        for(Method m:methods){
            System.out.println(m);
        }
        methods=stuClass.getDeclaredMethods();
        for(Method m:methods){
            System.out.println(m);
        }
        //获取共有的show1()方法
        Method m=stuClass.getMethod("show1", String.class);
        System.out.println(m);
        Object obj=stuClass.getConstructor().newInstance();
        m.invoke(obj,"刘亦菲");

        m=stuClass.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);
        Object result=m.invoke(obj,20);
        System.out.println("返回值:"+result);
    }
}

反射方法的其他使用–通过反射越过泛型检查:
泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的
测试类:

/*
 * 通过反射越过泛型检查
 * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
 */
public class Demo {
	public static void main(String[] args) throws Exception{
		ArrayList<String> strList = new ArrayList<>();
		strList.add("aaa");
		strList.add("bbb");
	//	strList.add(100);
		//获取ArrayList的Class对象,反向的调用add()方法,添加数据
		Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
		//获取add()方法
		Method m = listClass.getMethod("add", Object.class);
		//调用add()方法
		m.invoke(strList, 100);
		//遍历集合
		for(Object obj : strList){
			System.out.println(obj);
		}
	}
}
控制台输出:
aaa
bbb
100

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值