目录
Junit单元测试
一、测试分类
二、Junit概述
三、Junit的使用
类加载器
一、类加载器概述
二、类的加载时机
三、类的加载器
反射(Reflection)
一、反射(Reflection)机制概述
静态语言
利用反射机制提供的功能
反射相关的主要API
二、Class类及获取Class
三、 反射获取构造方法
四、反射获取空参构造方法并运行
五、 反射带参构造方法并运行
六、反射创建对象的快捷方式
七、 反射获取私有构造方法并运行
八、反射获取成员方法
九、反射调用get_set方法
十、反射获取私有方法并运行
十一、 反射案例分析
十二、反射案例代码实现
十三、 反射案例代码实现优化
注解
一、 注解的介绍
二、 自定义注解
三、自定义注解基本使用
四、 自定义注解使用的注意事项
五、 元注解@Target介绍
六、元注解@Retention介绍
七、注解解析获取到类上的注解
八、注解解析获取到方法上的注解
黑盒测试:不需要写代码,输入值看程序是否能够输出期望的值。
白盒测试:需要写代码,关注程序具体的执行流程。
Junit是java编程语言的单元测试工具。Junit是一个非常重要的测试工具
Junit是一个开放源代码的测试工具。
提供注解来识别测试方法:@Test、@Before、@After
Junit测试可以让代码编写更快,并提高质量
Junit简洁,花费时间较少
Junit在一个条中显示进度,如果运行良好是绿色,反之为红色
步骤:
1.模块名称上右键/new/directory/输入名称lib确定
2.把junit的jar包复制到文件夹lib中
3.文件夹lib上右键/Add as Library/在对话框中输入以下内容后/ok
Name: 输入lib
Level:输入Module Library
Add to module: 输入当前模块名
============================================================
//只能用在非静态无返回值无参数的方法上
/* 运行:
方法上右键运行,运行的是含有@Test注解的方法
类上右键运行,运行的是类当中含有@Test注解的所有方法
绿条: 正常运行
红条: 出现问题,异常了
常用注解
- @Test,用于修饰需要执行的测试方法
- @Before,修饰的方法会在测试方法之前被自动执行
- @After,修饰的方法会在测试方法执行之后自动被执行
*/
import org.junit.Test;
public class DemoJunit {
@Test
public void method(){
System.out.println("method...");
}
@Test
public void fun(){
System.out.println("fun...");
}
@Test
public void show(){
System.out.println("show...");
}
@Before
public void before(){
System.out.println("before...");
}
@After
public void after(){
System.out.println("after...");
}
}
=========================================================
运行结果:
before...
method...
after...
before...
fun...
after...
before...
show...
after...
类加载器:负责将.class文件(存储的物理文件)加载到内存中
public class DemoClass {
public static void main(String[] args) {
// 1. 创建类的实例。
DemoClassFather father = new DemoClassFather();
// 2. 类的静态变量,或者为静态变量赋值。
System.out.println(DemoClassFather.str);
// 3. 类的静态方法。
DemoClassFather.method();
// 4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
// 5. 初始化某个类的子类。
new Son();
// 6. 直接使用java.exe命令来运行某个主类。
}
}
======================================================================
//父类
public class DemoClassFather {
//静态变量
static String str ="haha";
//静态代码块
static {
System.out.println("DemoClassFather被加载。。。");
}
//静态方法
public static void method(){
System.out.println("method.....");
}
}
class Son extends DemoClassFather{
static {
System.out.println("Son被加载。。。。");
}
}
类加载器:
1.作用:
负责把.class文件加载到内存的方法区中.
将class文件(硬盘)加载到内存生成Class对象。
2.组成:
(1)BootstrapClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
引导类加载器BootstrapClassLoader:
用C++编写的,是JVM自带的类加载器,
负责Java平台核心库,用来加载核心类库。该加载器无法直接获取
比如System,String等。
(2)ExtClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载
在JDK中JRE的lib目录下ext目录
(3)AppClassLoader 系统类加载器/应用类加载器
Java语言编写的类加载器,负责加载我们定义的类和第三方jar包中的类。
3.获取类加载器
java.lang.Class类
成员方法:
public ClassLoader getClassLoader(): 返回该类的类加载器。
java.lang.ClassLoader类
成员方法:
public ClassLoader getParent(): 返回委托的父类加载器。
4.类加载器加载机制:
双亲委派机制: 谁用谁加载
比如:
public Person {
String s = name;
DNSNameService
...
}
java Person
Person类是由AppClassLoader加载
Person类内部使用String,原则上来讲,应该由AppClassLoader加载
但是,先找父加载器ExtClassLoader,不负责
ExtClassLoader找父加载器BootstrapClassLoader,负责
Person类内部使用DNSNameService,原则上来讲,应该由AppClassLoader加载
但是,先找父加载器ExtClassLoader,负责
不管怎样? .class文件只能被加载一次
注意:
BootstrapClassLoader 根类/引导类/核心类加载器 String,System
ExtClassLoader 扩展类加载器 DNSNameService
AppClassLoader 系统类加载器/应用类加载器
它们之间不存在继承关系,最终的父类 java.lang.ClassLoader
================================================================================
public class Demo03ClassLoader {
@Test
public void boot(){
//String 由BootstrapClassLoader加载,C++实现,不让我们获取
ClassLoader c = String.class.getClassLoader();
System.out.println(c);//null
}
@Test
public void ext() {
//由扩展类加载器加载:获取扩展类的class对象
ClassLoader c = DNSNameService.class.getClassLoader();
System.out.println(c);//sun.misc.Launcher$ExtClassLoader@452b3a41
}
@Test
public void app(){
//由App类加载器加载
ClassLoader c = Demo03ClassLoader.class.getClassLoader();
System.out.println(c);//sun.misc.Launcher$AppClassLoader@18b4aac2
}
@Test
public void app2() {
//由App类加载器加载:
ClassLoader c = Demo03ClassLoader.class.getClassLoader();
System.out.println(c);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader p = c.getParent();//委托父加载器
System.out.println(p);//sun.misc.Launcher$ExtClassLoader@452b3a41
ClassLoader pp = p.getParent();
System.out.println(pp);//null
}
}
注意: 1.当第一次使用类的信息时,该类的.class文件,会被加载到内存中的方法区中 2.jvm同时为加载到内存方法区的.class文件,创建一个Class类型的对象,该对象被保存在堆内存中 相当于堆内存中的Class类型的对象,指向了方法区的.class文件 3.一个类的.class文件,只能被加载一次,所以对应的Class类型的对象,只有一个 4.任意类型(基本类型/引用类型)都有对应的Class类型的对象 什么叫做反射呢? 通过获取Class类型的对象,从而操作对应的.class文件 说白了: 通过Class类型的对象获取.class文件中的成员变量/成员方法/构造方法并执行
被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构。
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
在运行时判断任意一个对象所属的类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在运行时获取泛型信息 在运行时调用任意一个对象的成员变量和方法 在运行时处理注解 生成动态代理
java.lang.Class:代表一个类 java.lang.reflect.Method:代表类的方法 java.lang.reflect.Field:代表类的成员变量 java.lang.reflect.Constructor:代表类的构造器
import org.junit.Test;
import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/*
获取Class对象的方式
方式一:
java.lang.Object类,成员方法:
public Class getClass(): 获取Class类型的对象
方式二:
任意类型(基本/引用),隐藏的class属性,获取该类对应的Class对象
方式三:
java.lang.Class类,静态方法:
public static Class> forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
参数:
String className: 类/接口 的全名称(包名+类/接口名)
建议使用方式三:
参数是String,可以写在配置文件中
Class类的成员方法:
public String getSimpleName(): 获得简单类名,只是类名,没有包
public String getName(): 获取完整类名,包含包名+类名
*/
public class ReflectionTest {
@Test
public void test3() throws ClassNotFoundException {
//方式一:
Class c1 = Person.class;
System.out.println(c1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class c2 = p1.getClass();
System.out.println(c2);
//方式三:调用Class的静态方法:forName(String classPath)
Class c3 = Class.forName("www.gh110.com");
// c3 = Class.forName("www.123.com");
System.out.println(c3);
System.out.println(c1 == c2);
System.out.println(c1 == c3);
//方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class c4 = classLoader.loadClass("www.gh110.com");
System.out.println(c4);
System.out.println(c1 == c4);
}
}
反射获取构造方法
步骤:
1.获取Class类型的对象(推荐使用: Class.forName)
2.java.lang.Class类,成员方法: 作用获取构造方法对象的
public Constructor[] getConstructors():
获取所有的public修饰的所有构造方法
每个构造方法,被封装成了一个Constructor对象,存储到数组中,并返回数组
public Constructor[] getDeclaredConstructors():
获取所有的构造方法(包含public,默认,protected,private修饰的)
每个构造方法,被封装成了一个Constructor对象,存储到数组中,并返回数组
public Constructor getConstructor(Class... parameterTypes)
根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
参数是可变参数,调用此方法时,可以不写参数,获取的空参构造
可以写参数,给定的参数必须是Class对象
比如:
参数 String name,int age
调用此方法: String.class,int.class
public Constructor getDeclaredConstructor(Class>... parameterTypes)
根据参数类型获取构造方法对象,可以获得private修饰的构造方法。
3.练习:
获取public修饰的所有构造方法对象
获取所有构造方法对象(包含private修饰的)
获取public修饰的空参构造方法对象
获取public修饰的第一个参数是String类型,第二个参数是int类型的构造方法对象
获取public修饰的参数是int类型的构造方法对象
=========================================================================================
public class Demo04GetConstructor {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐使用: Class.forName)
Class> clazz = Class.forName("domain.User");
//获取所有的public修饰的所有构造方法
Constructor>[] cons = clazz.getConstructors();
//增强for遍历
for (Constructor> con : cons) {
System.out.println(con);
}
System.out.println("----------------");
//获取public修饰的空参构造方法对象
Constructor> con1 = clazz.getConstructor();
System.out.println(con1);
//获取public修饰的第一个参数是String类型,第二个参数是int类型的构造方法对象
Constructor> con2 = clazz.getConstructor(String.class, int.class);
System.out.println(con2);
//获取public修饰的参数是int类型的构造方法对象
//报异常: NoSuchMethodException,没有这个方法异常,因为方法是private修饰
//Constructor> con3 = clazz.getConstructor(int.class);
//System.out.println(con3);
//获取private修饰的参数是int类型的构造方法对象
Constructor> con4 = clazz.getDeclaredConstructor(int.class);
System.out.println(con4);
}
}
反射获取空参构造方法并运行
步骤:
1.获取Class类型的对象(推荐使用: Class.forName)
2.Class对象调用getConstructor方法,获取空参构造方法对象
3.执行空参构造方法对象,从而创建一个具体的对象
java.lang.reflect.Constructor类,成员方法:
public Object newInstance(Object... params):
根据方法参数传递的具体数据,调用指定的构造方法,从而创建一个具体的对象
参数:
Object... params: 可变参数
传递数组,参数列表,不传参都可以
作用:
创建对象时,成员变量需要具体的数据
举例(空参构造):
不使用反射
User user = new User();
使用反射:
获取到了空参构造方法对象
Constructor con = ...;//空参构造方法被封装到了Constructor对象con中
//newInstance方法内部会把Constructor对象con中封装的空参构造执行一次,
//从而创建一个具体的对象出来
Object obj = con.newInstance();
举例(满参构造):------此处很重要,帮助大家理解------
不使用反射
User user = new User("zhangsan",18);
使用反射:
获取到了满参构造方法对象
Constructor con = ...;//满参构造方法被封装到了Constructor对象con中
//newInstance方法内部会把Constructor对象con中封装的满参构造执行一次,
//并且newInstance方法会把自己接收到的参数,交给Constructor对象con中封装的满参构造
//从而创建一个带数据的具体的对象出来
Object obj = con.newInstance("zhangsan",18);
=========================================================================================
public class Demo05NewInstance {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐使用: Class.forName)
Class clazz = Class.forName("domain.User");
//2.Class对象调用getConstructor方法,获取空参构造方法对象
Constructor con = clazz.getConstructor();
//3.执行空参构造方法对象,从而创建一个具体的对象
User user = (User)con.newInstance();
System.out.println(user);
}
}
反射带参构造方法并运行
步骤:
1.获取Class类型的对象(推荐: Class.forName(...))
2.Class类型的对象调用getConstructor方法,获取指定的构造方法对象
3.构造方法对象调用newInstance方法,创建一个具体的对象
4.打印对象
========================================================================
public class Demo06NewInstance {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐: Class.forName(...))
Class> clazz = Class.forName("domain.User");
//2.Class类型的对象调用getConstructor方法,获取指定的构造方法对象
//获取public修饰,指定参数类型的构造方法对象
Constructor con = clazz.getConstructor(String.class, int.class);
//3.构造方法对象调用newInstance方法,创建一个具体的对象
User user = (User) con.newInstance("张三",18);
//4.打印对象
System.out.println(user);
}
}
反射创建对象的快捷方式
步骤:
1.获取Class类型的对象(推荐: Class.forName(...))
2.Class对象调用newInstance方法,创建一个具体的对象
java.lang.Class类,成员方法:
public T newInstance()
创建此 Class 对象所表示的类的一个新实例。
底层原理:
(1)获取空参构造方法对象
(2)空参构造方法对象调用newInstance方法
3.打印对象
=======================================================================
public class Demo07NewInstance {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐: Class.forName(...))
Class clazz = Class.forName("domain.User");
//2.Class对象调用newInstance方法,创建一个具体的对象
User user = (User)clazz.newInstance();
//3.打印对象
System.out.println(user);
}
}
反射获取私有构造方法并运行 暴力反射
步骤:
1.获取Class类型的对象(推荐使用: Class.forName)
2.Class对象调用getDeclaredConstructor方法,获取指定参数私有构造方法对象
3.指定参数私有构造方法对象调用setAccessible方法,取消 Java 语言访问检查
4.指定参数私有构造方法对象调用newInstance方法,创建一个具体的对象
5.打印对象
注意:
java.lang.reflect.AccessibleObject类,成员方法:
public void setAccessible(boolean flag):
将此对象的 accessible 标志设置为指示的布尔值。
参数:
boolean flag
true: 取消 Java 语言访问检查 private 失效
false: 实施 Java 语言访问检查 private 有效
AccessibleObject类,子类:
Constructor,Field,Method类,都可以调用setAccessible方法
从而进行暴力反射
====================================================================================
public class Demo08NewInstance {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐使用: Class.forName)
Class clazz = Class.forName("domain.User");
//2.Class对象调用getDeclaredConstructor方法,获取指定参数私有构造方法对象
Constructor con = clazz.getDeclaredConstructor(int.class);
//3.指定参数私有构造方法对象调用setAccessible方法,取消 Java 语言访问检查
con.setAccessible(true);
//4.指定参数私有构造方法对象调用newInstance方法,创建一个具体的对象
User user = (User) con.newInstance(28);
//5.打印对象
System.out.println(user);
}
}
/*
Javabean类的介绍
1.所有成员变量必须private修饰
2.必须提供对应的get和set方法
3.必须提供空参构造(满参构造是可选的)
*/
public class User {
private String name;
private int age;
private char[] my2CharArray(String str) {
return str.toCharArray();
}
public int getSum(int a, int b) {
return a + b;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//空参构造
public User() {
}
//满参
public User(String name, int age) {
this.name = name;
this.age = age;
}
//只给name赋值
public User(String name) {
this.name = name;
}
//只给age赋值
private User(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/*
反射获取成员方法
步骤:
1.获取Class类型的对象(推荐使用: Class.forName)
2.java.lang.Class类,成员方法: 作用获取成员方法对象的
public Method[] getMethods()
获取所有的public修饰的成员方法,包括父类中。
每个成员方法被封装成为一个Method对象,存储到数组中,并返回
public Method getMethod(String methodName, Class... params)
根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
参数:
String methodName: 方法的名字
Class... params: 方法参数类型对应的Class对象
可变参数: 传递数组,参数列表,不传参
Method getDeclaredMethod(String name, Class... params)
根据方法名和参数类型获得一个方法对象,可以获取private修饰的
3.练习:
获取任意修饰符的所有成员方法
获取public修饰符的所有成员方法
获取public修饰的名称为toString的没有参数的方法
获取public修饰的名称为setName的参数为String类型的方法
获取public修饰的名称为getSum的参数为两个int类型的方法
获取public修饰的名称为my2CharArray的参数为String类型的方法
*/
public class Demo01GetMethod {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐使用: Class.forName)
Class> clazz = Class.forName("domain.User");
//2.获取所有public修饰包含父类的成员方法对象组成的数组
Method[] ms = clazz.getMethods();
//增强for遍历
for (Method m : ms) {
System.out.println(m);
}
System.out.println("----------------");
//获取public修饰的名称为toString的没有参数的方法
Method m1 = clazz.getMethod("toString");
System.out.println(m1);
//获取public修饰的名称为setName的参数为String类型的方法
Method m2 = clazz.getMethod("setName", String.class);
System.out.println(m2);
//获取public修饰的名称为getSum的参数为两个int类型的方法
Method m3 = clazz.getMethod("getSum", int.class, int.class);
System.out.println(m3);
//获取public修饰的名称为my2CharArray的参数为String类型的方法
//该方法private修饰,无法获取报出异常
//Method m4 = clazz.getMethod("my2CharArray", String.class);
//System.out.println(m4);
//获取private修饰的名称为my2CharArray的参数为String类型的方法
Method m5 = clazz.getDeclaredMethod("my2CharArray", String.class);
System.out.println(m5);
}
}
反射调用get_set方法
步骤:
1.获取Class类型的对象(推荐使用: Class.forName)
2.Class类型的对象调用newInstance方法,创建具体的对象
3.Class类型的对象调用getMethod方法,获取get和set方法对应的Method对象
4.get和set方法对应的Method对象调用invoke方法,执行对应的功能
java.lang.reflect.Method类,成员方法:
public Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
参数:
Object obj: 成员方法的调用必须由对象支持
Object... args: 执行成员方法时,需要的具体的参数
返回值类型:
java.lang.Object类型: 成员方法执行后的返回值类型(被提升为Object类型)
如果方法的返回值类型是void: 返回null
不使用反射:
User user = new User();
user.setName("张三");
使用反射:
已经获取到了setName方法对应的Method对象
已经把setName方法封装到Method对象setNameMethod中
Method setNameMethod = clazz.getMethod(...);
User user = new User();//通过反射创建也可以
//invoke方法内部使用invoke方法接收到的参数User对象user
//调用它内部封装的成员方法setName,同时调用该成员方法setName的时候传递参数"张三"
setNameMethod.invoke(user,"张三");
=========================================================================================
public class Demo02Invoke {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐使用: Class.forName)
Class> clazz = Class.forName("domain.User");
//2.Class类型的对象调用newInstance方法,创建具体的对象
User user = (User) clazz.newInstance();
//3.Class类型的对象调用getMethod方法,获取get和set方法对应的Method对象
Method setNameMethod = clazz.getMethod("setName", String.class);
Method setAgeMethod = clazz.getMethod("setAge", int.class);
Method getNameMethod = clazz.getMethod("getName");
Method getAgeMethod = clazz.getMethod("getAge");
//4.get和set方法对应的Method对象调用invoke方法,执行对应的功能
//执行set方法,给成员变量赋值
Object value = setNameMethod.invoke(user, "张三");
System.out.println(value);//null
value = setAgeMethod.invoke(user, 38);
System.out.println(value);//null
//System.out.println(user);
//执行get方法,获取成员变量的值
String name = (String) getNameMethod.invoke(user);
int age = (int) getAgeMethod.invoke(user);
System.out.println(name+"::::"+age);
}
}
反射获取私有方法并运行
步骤:
1.获取Class类型的对象(推荐使用: Class.forName)
2.Class类型的对象调用newInstance方法,创建具体的对象
3.Class类型的对象调用getDeclaredMethod方法,获取private修饰的方法对应的Method对象
4.private修饰的方法对应的Method对象调用setAccessible方法,取消 Java 语言访问检查
5.private修饰的方法对应的Method对象调用调用invoke方法,执行对应的功能
注意:
java.lang.reflect.AccessibleObject类,成员方法:
public void setAccessible(boolean flag):
将此对象的 accessible 标志设置为指示的布尔值。
参数:
boolean flag
true: 取消 Java 语言访问检查 private 失效
false: 实施 Java 语言访问检查 private 有效
AccessibleObject类,子类:
Constructor,Field,Method类,都可以调用setAccessible方法
从而进行暴力反射
=========================================================================================
public class Demo03Invoke {
public static void main(String[] args) throws Exception {
//1.获取Class类型的对象(推荐使用: Class.forName)
Class> clazz = Class.forName("domain.User");
//2.Class类型的对象调用newInstance方法,创建具体的对象
User user = (User)clazz.newInstance();
//3.Class类型的对象调用getDeclaredMethod方法,获取private修饰的方法对应的Method对象
Method m = clazz.getDeclaredMethod("my2CharArray", String.class);
//4.private修饰的方法对应的Method对象调用setAccessible方法,取消 Java 语言访问检查
m.setAccessible(true);
//5.private修饰的方法对应的Method对象调用调用invoke方法,执行对应的功能
char[] chs = (char[]) m.invoke(user,"helloworld");
System.out.println(new String(chs));
}
}
/*
反射案例
需求:
写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
步骤:
1.定义配置文件config.properties,以属性名=属性值
className=itheima02.Dog
methodName=eat
注意:
配置文件config.properties必须写在src根路径下
2.需要把配置文件信息,加载到Properties集合对象中
3.获取类的全名称和方法名称
4.获取类的Class类型的对象
5.根据Class类型的对象获取要执行的方法对应的Method对象
6.Class类型的对象创建一个具体的对象出来
7.Method对象调用invoke方法,执行具体的功能
*/
//抽象父类
public abstract class Animal {
public abstract void eat();
}
//子类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼...");
}
}
//子类
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头....");
}
}
public class Demo02ReflectTest {
public static void main(String[] args) throws Exception {
//2.需要把配置文件信息,加载到Properties集合对象中
Properties props = new Properties();
props.load(new FileInputStream("day14\\src\\config.properties"));
//3.获取类的全名称和方法名称
//类的全名称
String className = props.getProperty("className");
//方法名称
String methodName = props.getProperty("methodName");
//4.获取类的Class类型的对象
Class> clazz = Class.forName(className);
//5.根据Class类型的对象获取要执行的方法对应的Method对象
Method eatMethod = clazz.getMethod("eat");
//6.Class类型的对象创建一个具体的对象出来
Object obj = clazz.newInstance();
//7.Method对象调用invoke方法,执行具体的功能
eatMethod.invoke(obj);
}
}
/*
反射案例
需求:
写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
步骤:
1.定义配置文件config.properties,以属性名=属性值
className=itheima02.Dog
methodName=eat
注意:
配置文件config.properties必须写在src根路径下
src下写的java源代码,项目完成后,给客户的是.class文件
配置文件会跟着.class文件,到类路径(存储.class文件的路径)下,同步的
2.需要把配置文件信息,加载到Properties集合对象中
3.获取类的全名称和方法名称
4.获取类的Class类型的对象
5.根据Class类型的对象获取要执行的方法对应的Method对象
6.Class类型的对象创建一个具体的对象出来
7.Method对象调用invoke方法,执行具体的功能
java.lang.Class类,成员方法:
public ClassLoader getClassLoader(): 返回该类的类加载器。
java.lang.ClassLoader类,成员方法:
public InputStream getResourceAsStream(String name): 返回读取指定资源的输入流。
指定资源: 类路径(该方法直接到类路径下扫描配置文件)
如果配置文件写在src根下:
String name: 只需要写文件名.扩展名 config.properties
如果配置文件写在src/itheima02下:
String name: itheima02/config.properties
*/
public class Demo03ReflectTest {
public static void main(String[] args) throws Exception {
//2.需要把配置文件信息,加载到Properties集合对象中
Properties props = new Properties();
//获取类加载器
ClassLoader classLoader = Demo03ReflectTest.class.getClassLoader();
//通过类加载器,获取资源的字节输入流
InputStream is = classLoader.getResourceAsStream("config.properties");
props.load(is);
//3.获取类的全名称和方法名称
//类的全名称
String className = props.getProperty("className");
//方法名称
String methodName = props.getProperty("methodName");
//4.获取类的Class类型的对象
Class> clazz = Class.forName(className);
//5.根据Class类型的对象获取要执行的方法对应的Method对象
Method eatMethod = clazz.getMethod("eat");
//6.Class类型的对象创建一个具体的对象出来
Object obj = clazz.newInstance();
//7.Method对象调用invoke方法,执行具体的功能
eatMethod.invoke(obj);
}
}
/*
反射案例
需求:
写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
步骤:
1.定义配置文件config.properties,以属性名=属性值
className=itheima02.Dog
methodName=eat
注意:
配置文件config.properties必须写在src根路径下
src下写的java源代码,项目完成后,给客户的是.class文件
配置文件会跟着.class文件,到类路径(存储.class文件的路径)下,同步的
2.需要把配置文件信息,加载到Properties集合对象中
3.获取类的全名称和方法名称
4.获取类的Class类型的对象
5.根据Class类型的对象获取要执行的方法对应的Method对象
6.Class类型的对象创建一个具体的对象出来
7.Method对象调用invoke方法,执行具体的功能
java.lang.Class类,成员方法:
public ClassLoader getClassLoader(): 返回该类的类加载器。
java.lang.ClassLoader类,成员方法:
public InputStream getResourceAsStream(String name): 返回读取指定资源的输入流。
指定资源: 类路径(该方法直接到类路径下扫描配置文件)
如果配置文件写在src根下:
String name: 只需要写文件名.扩展名 config.properties
如果配置文件写在src/itheima02下:
String name: itheima02/config.properties
java.util.ResourceBundle类: 获取资源的类
注意: 该类是抽象类,不能直接创建对象,可以创建子类对象
发现:
该类中提供了静态方法,用于获取该抽象类的子类对象
public static final ResourceBundle getBundle(String baseName):
获取抽象类ResourceBundle的子类对象
参数:
String baseName: 如果配置文件写在src根下,此处只需要写文件名(不需要写扩展名)
ResourceBundle类的成员方法:
public final String getString(String key):
根据String类型的属性名,获取String类型属性值(根据键找值)
*/
public class Demo04ReflectTest {
public static void main(String[] args) throws Exception {
//创建子类管理类ResourceBundle的对象
ResourceBundle resourceBundle = ResourceBundle.getBundle("config");
//3.获取类的全名称和方法名称
String className = resourceBundle.getString("className");
String methodName = resourceBundle.getString("methodName");
//4.获取类的Class类型的对象
Class> clazz = Class.forName(className);
//5.根据Class类型的对象获取要执行的方法对应的Method对象
Method eatMethod = clazz.getMethod("eat");
//6.Class类型的对象创建一个具体的对象出来
Object obj = clazz.newInstance();
//7.Method对象调用invoke方法,执行具体的功能
eatMethod.invoke(obj);
}
}
1.概念: 注解(Annotation): 也叫元数据。一种代码级别的说明。 它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。 它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。 2.作用分类: (1)编写文档:通过代码里标识的注解生成文档【例如,生成文档doc文档】 (2)代码分析:通过代码里标识的注解对代码进行分析【例如,注解的反射】 (3)编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【例如,Override】 3.常见注解 (1)@author:用来标识作者名 (2)@version:用于标识对象的版本号,适用范围:文件、类、方法。 (3)@Override:用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。 (4)@FunctionalInterface: 检测是否是函数式接口的
Locale: zh_CN
Other command line arguments:
-encoding UTF-8 -charset UTF-8 -windowtitle "IOUtils类的使用" -link http://docs.Oracle.com/javase/7/docs/api
格式: public @interface 注解名 { 属性集 } 1.空注解: 没有任何属性 看MyAnno01 2.有属性集的注解: 看MyAnno02 属性的定义格式一:数据类型 属性名(); 没有默认值的属性 属性的定义格式二:数据类型 属性名() default 默认值; 有默认值的属性 3.注解属性可以选择的数据类型: 8种基本类型,String类型,枚举类型,注解类型,Class类型以及以上任意类型对应的一维数组
public class Demo03Annotation {
}
//空注解
public @interface MyAnno01 {
}
//定义有属性集的注解
public @interface MyAnno02 {
String name();//String 类型的属性 name,没有默认值
int age() default 18;//int 类型的属性 age,默认值 18
String[] hobbies();//String 类型的数组 hobbies
MyAnno01 myAnno01();//注解类型
}
空注解使用格式: 直接使用 @注解名称 有属性集注解使用格式: @注解名称(属性名1=属性值1,属性名2=属性值2,属性名3={元素1,元素2...}) 注意事项: 1.空注解可以直接使用 2.一个注解只能在一个位置上使用一次,一个位置上可以使用多个不同的注解 3.如果注解有属性,那么必须给属性赋值才能使用键值对的方式赋值 属性名=属性值 多个属性,隔开。如果属性是数组类型 并且只有一个属性值 那么{}可以省略 如果多个属性值 {}不能省略 4.如果属性没有默认值 必须赋值 如果有默认值 可以不赋值
@MyAnno01
//@MyAnno01 //同一位置不能使用第二次
@MyAnno02(name="张三",age=28,hobbies={"看球","撸代码"},myAnno01=@MyAnno01)
public class Demo04UseAnnotation {
@MyAnno01
@MyAnno02(name="李四",hobbies = "看球",myAnno01 = @MyAnno01)
public static void main(String[] args) {
}
}
注意事项: ----重点---- MyAnno03,MyAnno04,MyAnno05 - 当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值, 无论value是单值元素还是数组类型。 - 如果属性有默认值,则使用注解的时候,这个属性可以不用赋值。 - 如果属性没有默认值,那么在使用注解时一定要给属性赋值。
@MyAnno03("张三")
@MyAnno04 //有默认值的属性,不管属性名是什么,都可以省略
@MyAnno05({"打乒乓球","撸代码"})
public class Demo05AnnotationNotice {
@MyAnno04("李四")
public static void main(String[] args) {
}
}
public @interface MyAnno03 {
String value();
}
public @interface MyAnno04 {
String value() default "李四";
}
public @interface MyAnno05 {
String[] value();
}
1.作用: 用来限制自定义注解的生命周期(只存在于源代码中,存在于class文件中,存在于运行时期的内存中) 用来限制自定义注解的使用范围(只能写在类上,只能写方法上,既能写在类上又能写在方法上等等) 2.限制自定义注解的使用范围(在什么位置可以使用) 元注解(限制自定义注解的注解)之@Target,源代码: public @interface Target { ElementType[] value(); } java.lang.annotation.ElementType 枚举 可以理解为类,里面都是静态内容,直接用类名/枚举名调用 TYPE: 用在类,接口上 FIELD:用在成员变量上 METHOD: 用在方法上 PARAMETER:用在参数上 CONSTRUCTOR:用在构造方法上 LOCAL_VARIABLE:用在局部变量上
@MyAnno06
public class Demo05YuanAnnotation {
//@MyAnno06 //错误了,该注解只能写在类/方法上
private String name;
@MyAnno06
public static void main(String[] args) {
}
}
/*
限制注解的使用范围: 指定写在类上/方法上
使用元注解: Target
*/
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno06 {
}
1.作用: 用来限制自定义注解的生命周期(只存在于源代码中,存在于class文件中,存在于运行时期的内存中) 用来限制自定义注解的使用范围(只能写在类上,只能写方法上,既能写在类上又能写在方法上等等) 2.限制自定义注解的生命周期(存在到什么时候,源代码中,生成的.class文件中,还是运行.class文件的时) 元注解(限制自定义注解的注解)之@Retention public @interface Retention { RetentionPolicy value(); } 该注解是单属性的,属性名value,使用时,可以不写属性名 java.lang.annotation.RetentionPolicy 枚举 可以理解为类,里面都是静态内容,直接用类名/枚举名调用 SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了。 CLASS:注解存在于Java源代码、编译以后的字节码文件中,运行的时候内存中没有,默认值。 RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以通过反射获取该注解。
@MyAnno07
//@MyAnno08//只能用在方法上/成员变量上
public class Demo06YuanAnnotation {
//@MyAnno07 //错误了,该注解只能写在类/方法上
@MyAnno08
private String name;
@MyAnno07
@MyAnno08
public static void main(String[] args) {
}
}
/*
定义注解:
只能用在方法上/类上
保留在class文件中,运行时期内存中没有
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface MyAnno07 {
}
/*
定义注解:
只能用在方法上/成员变量上
保留在源文件中,class文件中没有
*/
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnno08 {
}
注解本质上就是一个接口,该接口默认继承Annotation接口 java.lang.annotation.Annotation接口:所有注解类型的公共接口,类似所有类的父类是Object。 前面用过的元注解 @Target @Retention 就是该接口的实现类对象 需求说明 1. 定义注解Books,要求如下: - 包含属性:String value() 书名 - 包含属性:double price() 价格,默认值为 100 - 包含属性:String[] authors() 多位作者 - 限制注解使用的位置:类和成员方法上 - 指定注解的有效范围:RUNTIME 2. 定义BookStore类,在类和成员方法上使用Book注解 3. 定义Demo06AnnotationTest测试类获取Book注解上的数据 获取类上定义的注解 java.lang.Class类,成员方法public A getAnnotation(Class annotationClass)如果该元素的指定注解类型的注解存在于此对象(Class对象)上,则返回这个注解,否则返回 null;获得当前对象上指定的注解对象。 public boolean isAnnotationPresent(Class annotationClass):判断当前对象(Class对象)是否有指定的注解,有则返回true,否则返回false。 步骤: 1.获取类的Class类型的对象 2.Class类型的对象调用方法,判断是否具有Books注解 3.如果有,Class类型的对象调用方法获取Books注解对象 4.打印Books注解对象的属性值
/*
定义注解Books,要求如下:
- 包含属性:String value() 书名
- 包含属性:double price() 价格,默认值为 100
- 包含属性:String[] authors() 多位作者
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:RUNTIME
*/
//限制注解使用的位置:类和成员方法上
@Target({ElementType.TYPE,ElementType.METHOD})
//指定注解的有效范围:RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface Books {
String value();//书名
double price() default 100;//价格,默认值为 100
String[] authors();//多位作者
}
@Books(value = "面向对象",price = 180,authors = {"响哥","刚哥"})
public class BookStore {
@Books(value = "jdk8新特性",authors = "响哥")
public void show() {
}
}
//获取类上注解信息的测试类
public class Demo06AnnotationTest {
@Test
public void getParseClassAnnotation() throws Exception {
//1.获取类的Class类型的对象
Class> clazz = Class.forName("itheima06.BookStore");
//2.Class类型的对象调用方法,判断是否具有Books注解
if(clazz.isAnnotationPresent(Books.class)) {
//3.如果有,Class类型的对象调用方法获取Books注解对象
Books booksAnno = clazz.getAnnotation(Books.class);
//4.打印Books注解对象的属性值
String bookName = booksAnno.value();
double price = booksAnno.price();
String[] authors = booksAnno.authors();
System.out.println(bookName+"::"+price+"::"+ Arrays.toString(authors));
}
}
}
注解本质上就是一个接口,该接口默认继承Annotation接口 java.lang.annotation.Anontation接口:所有注解类型的公共接口,类似所有类的父类是Object。 前面用过的元注解 @Target @Rentention 就是该接口的实现类对象 需求说明 1. 定义注解Books,要求如下: - 包含属性:String value() 书名 - 包含属性:double price() 价格,默认值为 100 - 包含属性:String[] authors() 多位作者 - 限制注解使用的位置:类和成员方法上 - 指定注解的有效范围:RUNTIME 2. 定义BookStore类,在类和成员方法上使用Book注解 3. 定义Demo06AnnotationTest测试类获取Book注解上的数据 获取类上定义的注解 java.lang.reflect.Method类, 成员方法public A getAnnotation(Class annotationClass) 如果该元素的指定注解类型的注解存在于此对象(Class对象)上,则返回这个注解,否则返回 null 获得当前对象上指定的注解对象。 public boolean isAnnotationPresent(Class annotationClass):判断当前对象(Class对象)是否有指定的注解,有则返回true,否则返回false。 步骤: 1.获取类的Class类型的对象 2.Class类型的对象调用方法,获取方法对象 3.方法对象调用方法,判断是否具有Books注解 4.如果有,方法对象调用方法获取Books注解对象 5.打印Books注解对象的属性值 总结: 1、java.lang.reflect.AnnotatedElement接口: 规定操作注解的方法 2、public abstract boolean isAnnotationPresent(Class clazz): 判断是否含有指定参数类型的注解,有返回true,没有返回false 3、public abstractT getAnnotation(Class clazz):获取指定参数类型的注解对象,没有获取到的,返回null 参数:Class clazz: 注解的Class对象 常用实现类:Class,Method,Constructor,Field 类 都实现了以上方法 总结: 1.获取类上的注解: 使用Class对象直接获取 2.获取方法上的注解: 使用Method对象直接获取 3.获取构造方法上的注解: 使用Constructor对象直接获取 4.获取成员变量上的注解: 使用Field对象直接获取 注意:Method对象/Constructor对象/Field对象 都被必须由Class对象获取
public class Demo07AnnotationTest {
@Test
public void parseMethodAnnotation() throws Exception {
//1.获取类的Class类型的对象
Class> clazz = Class.forName("itheima06.BookStore");
//2.Class类型的对象调用方法,获取方法对象
Method showMethod = clazz.getMethod("show");
//2.方法对象调用方法,判断是否具有Books注解
if(showMethod.isAnnotationPresent(Books.class)) {
//3.如果有,方法对象调用方法获取Books注解对象
Books booksAnno = showMethod.getAnnotation(Books.class);
//4.打印Books注解对象的属性值
String bookName = booksAnno.value();
double price = booksAnno.price();
String[] authors = booksAnno.authors();
System.out.println(bookName+"::"+price+"::"+ Arrays.toString(authors));
}
}
}