1.反射
1.概念
(1)当我们想要使用别人的东西/查看某些资源的时候,可以使用反射技术
再比如,开发时,有时候不能看到源代码,也可以通过反射获取
2.反射的前提:获取字节码对象
(1)在java中可以将对象分为两类:字节码对象和实例对象
(2)字节码对象获取的三种方式:
Class.forName(“目标类的全路径”)
类名.class
目标类对象.getClass()
注意:字节码对象是获取目标对象所有信息的入口
3.反射的常用方法
3.1 通过单元测试方法,获取目标类对应的字节码对象
(1)获取包对象 System.out.println(student2.getPackage());
(2)先获取包对象,再获取包对象的名字
System.out.println(student3.getPackage().getName());
(3)获取字节码对象
System.out.println(student1);
(4)打印全路径名
System.out.println(student1.getName());
(5)打印只有目标类的类名
System.out.println(student2.getSimpleName());
//1.通过单元测试方法,获取目标类Student对应的字节码对象
@Test
public void getCalzz() throws ClassNotFoundException {
Class<?> student1 = Class.forName("cn.tedu.review.Student");
Class<?> student2 = Student.class;
Class<?> student3 = new Student().getClass();
/*1>获取字节码对象*/
System.out.println(student1);//class cn.tedu.review.Student
/*2>获取通过字节码对象获取到的包对象*/
System.out.println(student2.getPackage());//package cn.tedu.review
/*3>获取通过包对象获取到的包的名字*/
System.out.println(student3.getPackage().getName());//cn.tedu.review
/*4>获取通过字节码对象获取到的全路径名*/
System.out.println(student1.getName());//cn.tedu.review.Student
/*5>获取通过字节码对象获取到的类名*/
System.out.println(student2.getSimpleName());//Student
}
3.2 通过单元测试方法,练习引用类型数组的定义与遍历
//2.通过单元测试方法,练习引用类型数组的定义与遍历
@Test
public void getStu() throws ClassNotFoundException {
//2.1 创建Student三个对象
Student s1=new Student("张三",3);
Student s2=new Student("李四",4);
Student s3=new Student("王五",5);
//2.2 创建数组,将刚刚创建的三个对象存入数组中
Student[] s=new Student[]{s1,s2,s3};
//2.3 直接打印数组,拿到没一个学生对象,做进一步操作
System.out.println(Arrays.toString(s));
//2.4 遍历学生数组,拿到没一个学生对象
for (Student n:s ) {
//System.out.println(n);//拿到的是对象
n.sunDay(3);
System.out.println(n.age);
System.out.println(n.getName());
}
}
3.3 通过单元测试方法,获取目标类的成员方法
(1)获取所有可见方法,包括继承方法
//3.通过单元测试方法,获取Student类中的成员方法
@Test
public void getFunction() throws ClassNotFoundException {
//3.1 获取字节码对象
Class<?> s1 = Class.forName("cn.tedu.review.Student");
Class<?> s2 = Student.class;
Class<?> s3 = new Student().getClass();
//3.2 通过字节码对象,获取目标类的成员方法们
Method[] ms = s1.getMethods();//获取所有可见的方法,包括继承的方法
System.out.println(Arrays.toString(ms));
//3.3 通过高效for循环遍历数组,拿到一个方法对象
for(Method m:ms){
System.out.println(m);//直接打印遍历到的方法对象
System.out.println(m.getName());//通过方法对象获取方法名
Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组
System.out.println(pt);
}
Method[] dm = s1.getDeclaredMethods();
}
3.4 通过单元测试方法,获取目标类的构造方法
//4.通过单元测试方法,获取Student类中的构造方法
@Test
public void getConstrutor() throws ClassNotFoundException {
//4.1 获取字节码对象
Class<?> s = Class.forName("cn.tedu.review.Student");
//4.2 通过字节码对象获取目标类的构造方法们
Constructor<?>[] cs = s.getConstructors();
//4.3 通过高效for循环遍历数组
for (Constructor c:cs){
System.out.println(c);
System.out.println(c.getName());//打印本轮遍历到的构造方法的名字
Class[] pt = c.getParameterTypes();//通过遍历到的构造函数对象获取构造函数参数的类型
System.out.println(pt);
}
}
3.5 通过单元测试方法,获取目标类的成员变量
//5.通过单元测试方法,获取Student类中的成员变量
@Test
public void getFie(){
//5.1 获取字节码对象
Class<?> s1 = Student.class;
//5.2 通过字节码对象获取成员变量们
Field[] fs = s1.getFields();
//5.3 遍历数组
/**
* 注意:目前成员变量的修饰符必须是public才能获取到
* 默认修饰符也是获取不到的
*/
for (Field f:fs) {
System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
System.out.println(f.getType());//通过本轮循环的字段对象获取字段类型
}
}
3.6 通过单元测试方法,创建目标类对象
//6.通过单元测试方法,创建Student目标类的对象
@Test
public void getObject() throws Exception{
//6.1 获取字节码对象
Class<?> s = Student.class;
//6.2 通过反射技术,创建目标类的对象
/**
* 反射创建对象方案1:通过触发目标类的无参构造来创建对象
* 对象创建了,但是没有值
*/
Object o = s.newInstance();//这一步已经获取了对象
System.out.println(o);//Student{name='null', age=0}
//6.3 尝试通过其他构造函数来创建对象
/**
* 反射创建对象方案2:通过触发目标类的全参构造创建对象:
* 1.先获取指定的构造函数,注意需要指定构造函数的参数,传入的是.class字节码对象
* 2.通过刚刚获取到的构造函数对象,创建Student目标类的对象,并给对象的属性赋值
*/
//6.3.1 想要其他构造,获取方式:指定参数列表来获取
Constructor<?> ct = s.getConstructor(String.class, int.class);
System.out.println(ct);
//6.3.2 通过构造函数,来创建对象
Object o2 = ct.newInstance("海绵宝宝", 12);
System.out.println(o2);
}
完整代码
package cn.tedu.review;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 这个类用来复写反射的测试类
*/
public class TestReflect {
/**
* 1.单元测试方法:public+void+没有参数+@Test
*/
//1.通过单元测试方法,获取目标类Student对应的字节码对象
@Test
public void getCalzz() throws ClassNotFoundException {
Class<?> student1 = Class.forName("cn.tedu.review.Student");
Class<?> student2 = Student.class;
Class<?> student3 = new Student().getClass();
/*1>获取字节码对象*/
System.out.println(student1);//class cn.tedu.review.Student
/*2>获取通过字节码对象获取到的包对象*/
System.out.println(student2.getPackage());//package cn.tedu.review
/*3>获取通过包对象获取到的包的名字*/
System.out.println(student3.getPackage().getName());//cn.tedu.review
/*4>获取通过字节码对象获取到的全路径名*/
System.out.println(student1.getName());//cn.tedu.review.Student
/*5>获取通过字节码对象获取到的类名*/
System.out.println(student2.getSimpleName());//Student
}
//2.通过单元测试方法,练习引用类型数组的定义与遍历
@Test
public void getStu() throws ClassNotFoundException {
//2.1 创建Student三个对象
Student s1=new Student("张三",3);
Student s2=new Student("李四",4);
Student s3=new Student("王五",5);
//2.2 创建数组,将刚刚创建的三个对象存入数组中
Student[] s=new Student[]{s1,s2,s3};
//2.3 直接打印数组,拿到没一个学生对象,做进一步操作
System.out.println(Arrays.toString(s));
//2.4 遍历学生数组,拿到没一个学生对象
for (Student n:s ) {
//System.out.println(n);//拿到的是对象
n.sunDay(3);
System.out.println(n.age);
System.out.println(n.getName());
}
}
//3.通过单元测试方法,获取Student类中的成员方法
@Test
public void getFunction() throws ClassNotFoundException {
//3.1 获取字节码对象
Class<?> s1 = Class.forName("cn.tedu.review.Student");
Class<?> s2 = Student.class;
Class<?> s3 = new Student().getClass();
//3.2 通过字节码对象,获取目标类的成员方法们
Method[] ms = s1.getMethods();//获取所有可见的方法,包括继承的方法
System.out.println(Arrays.toString(ms));
//3.3 通过高效for循环遍历数组,拿到一个方法对象
for(Method m:ms){
System.out.println(m);//直接打印遍历到的方法对象
System.out.println(m.getName());//通过方法对象获取方法名
Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组
System.out.println(pt);
}
Method[] dm = s1.getDeclaredMethods();
}
//4.通过单元测试方法,获取Student类中的构造方法
@Test
public void getConstrutor() throws ClassNotFoundException {
//4.1 获取字节码对象
Class<?> s = Class.forName("cn.tedu.review.Student");
//4.2 通过字节码对象获取目标类的构造方法们
Constructor<?>[] cs = s.getConstructors();
//4.3 通过高效for循环遍历数组
for (Constructor c:cs){
System.out.println(c);
System.out.println(c.getName());//打印本轮遍历到的构造方法的名字
Class[] pt = c.getParameterTypes();//通过遍历到的构造函数对象获取构造函数参数的类型
System.out.println(pt);
}
}
//5.通过单元测试方法,获取Student类中的成员变量
@Test
public void getFie(){
//5.1 获取字节码对象
Class<?> s1 = Student.class;
//5.2 通过字节码对象获取成员变量们
Field[] fs = s1.getFields();
//5.3 遍历数组
/**
* 注意:目前成员变量的修饰符必须是public才能获取到
* 默认修饰符也是获取不到的
*/
for (Field f:fs) {
System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
System.out.println(f.getType());//通过本轮循环的字段对象获取字段类型
}
}
//6.通过单元测试方法,创建Student目标类的对象
@Test
public void getObject() throws Exception{
//6.1 获取字节码对象
Class<?> s = Student.class;
//6.2 通过反射技术,创建目标类的对象
/**
* 反射创建对象方案1:通过触发目标类的无参构造来创建对象
* 对象创建了,但是没有值
*/
Object o = s.newInstance();//这一步已经获取了对象
System.out.println(o);//Student{name='null', age=0}
//6.3 尝试通过其他构造函数来创建对象
/**
* 反射创建对象方案2:通过触发目标类的全参构造创建对象:
* 1.先获取指定的构造函数,注意需要指定构造函数的参数,传入的是.class字节码对象
* 2.通过刚刚获取到的构造函数对象,创建Student目标类的对象,并给对象的属性赋值
*/
//6.3.1 想要其他构造,获取方式:指定参数列表来获取
Constructor<?> ct = s.getConstructor(String.class, int.class);
System.out.println(ct);
//6.3.2 通过构造函数,来创建对象
Object o2 = ct.newInstance("海绵宝宝", 12);
System.out.println(o2);
}
}
4.暴力反射
package cn.tedu.review;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 本类用来测试暴力反射
*/
public class TestReflect2{
//1.通过暴力反射获取与操作属性
@Test
public void getFileds() throws Exception{
//1.1 获取字节码对象
Class<Person> p1 = Person.class;
//1.2 获取私有属性
Field name = p1.getDeclaredField("name");
//1.3 根据刚刚获取到的属性对象,查看属性的信息
System.out.println(name);//直接打印获取到的字段对象
System.out.println(name.getType());//class java.lang.String
System.out.println(name.getType().getName());//java.lang.String
//1.4设置属性的值
//1.4.1 需要指定到底给哪个对象的name属性设置值,没有对象就创建对象
Person person1 = p1.newInstance();
//1.4.2 暴力反射,需要设置私有可见权限
name.setAccessible(true);
//1.4.3 通过字段对象给刚刚创建好的对象persn1设置值为小白
//name就是我们刚刚获取到的name属性
//set(m,n) -- m是给哪个对象的name属性设置值,n是设置的值是什么
name.set(person1,"小白");
//1.4.4 打印查看刚刚设置的值
System.out.println(name.get(person1));
}
//2.定义单元测试方法,利用暴力反射操作私有属性age
@Test
public void getFileds1() throws Exception{
//2.1定义单元测试方法
Class<Person> p= Person.class;
//2.2获取指定的私有属性对象
Field age = p.getDeclaredField("age");
//2.3根据获取到的属性对象,查看相关信息
System.out.println(age.getName());
System.out.println(age.getType().getName());
//2.4设置属性的值
Person person = p.newInstance();
age.setAccessible(true);
age.set(person,12);
System.out.println(age.get(person));
}
//3.创建单元测试方法,通过暴力反射获取与执行Person类的私有方法
@Test
public void getFunction() throws Exception {
//3.1获取字节码对象
Class<Person> p = Person.class;
//3.2通过字节码对象,获取私有方法对象
/**
* 如何确定要找的是哪个方法?方法名+参数列表
*/
Method save = p.getDeclaredMethod("save", int.class, String.class);
//3. 在执行获取到的方法之前,需要先指定给哪个对象做这个save()操作
//3.3.1 没有对象先创建对象
Person obj = p.newInstance();
//3.3.2 想要执行私有方法,也需要设置私有可见
/**
* 在执行私有的方法之前,需要设置私有可见的权限
*/
save.setAccessible(true);
//3.3.3 通过刚刚获取到的方法对象method给指定的对象obj进行操作
save.invoke(obj,222,"hhh");
}
}
2.内部类
2.1特点
1>内部类可以直接访问外部类中的成员,包括私有成员
2>外部类要访问内部类的成员,必须要建立内部类的对象
3>在成员位置的内部类为成员内部类
4>在局部位置的内部类为局部内部类
2.2 成员内部类
1>内部类可以直接使用外部类的资源,私有成员也可以
2>外部类如果想要使用内部类的资源,必须先创建内部类对象,通过内部类对象来调用内部类资源
3>创建内部类对象,使用内部类资源
外部类名.内部类名 对象名=外部类对象.内部类对象
练习1:测试内部类入门案例
package cn.tedu.innerclass;
/**
* 本类用于内部类的入门案例
*/
public class TestInner1 {
public static void main(String[] args) {
//3.1创建内部类的对象,使用内部类的资源
/**
* 外部类名.内部类名 对象名=外部类对象.内部类对象
*/
Outer.Inner oi=new Outer().new Inner();
//3.2通过创建好的内部类对象,使用内部类的资源
oi.delete();
System.out.println(oi.sum);
//3.3外部想要使用外部类的资源,需要通过外部类的对象
new Outer().play();
}
}
//1.创建外部类Outer
class Outer{
//1.1创建外部类的成员变量
String name;
private int age;
//1.2创建外部类的成员方法
public void play(){
System.out.println("outer...play()");
//5.测试外部类能否使用内部类的资源
// delete();//不能直接调用内部类的方法
// System.out.println(sum);//不能直接查看内部类的属性
/**
* 外部类如果想要使用内部类的资源,必须先创建内部类的对象
* 通过内部类对象调用内部类功能
*/
Inner in=new Inner();//直接创建内部类对象,无需指定外部类,已经在外部类里了
//in.delete();//通过内部类对象调用内部类方法
System.out.println(in.sum);//通过内部类对象调用内部类变量
}
//2.创建内部类 -- 外部类的一个特殊成员
class Inner{
//2.1创建内部类的成员变量
int sum=10;
//2.2定义内部类的成员方法
public void delete(){
System.out.println("Inner...delete()");
//4.测试内部类能否使用外部类的资源
play();//可以查看外部类的方法
System.out.println(name);//可以查看外部类的普通属性
System.out.println(age);//可以查看外部类的私有属性
}
}
}
2.3 成员内部类(private修饰)
1>成员内部类被Private修饰以后,无法被外界直接创建对象使用
2>我们可以创建外部类对象,通过外部类对象间接王文内部类资源
练习2:测试成员内部类被private修饰
package cn.tedu.innerclass;
/**
* 本类用于测试成员内部类被private修饰
*/
public class TestInner2 {
public static void main(String[] args) {
//4.创建内部类的对象
/**
* 2.如果成员内部类被private修饰,外部无法直接访问或者创建内部类的对象
*/
//7.间接访问,虽然不可以创建私有内部类的对象,但是可以创建外部类的对象
new Outer2().getInner2Eat();
// Outer2.Inter2 oi=new Outer2().new Inter2();
// oi.eat();
}
}
//1.创建外部类
class Outer2{
//6.提供一个公共的方法,在方法内部创建内部类的对象,调用内部类的功能
public void getInner2Eat(){
Inter2 in2=new Inter2();//可以在外部类里创建私有成员内部的对象
in2.eat();//通过创建好的内部类对象调用内部类功能
}
/**
* 1.成员内部类的位置在内里方法外
*/
//2.创建成员内部类
//5.用private修饰内部类
private class Inter2{
//3.创建内部类的普通方法
public void eat(){
System.out.println("Inner2...eat()");
}
}
}
2.4 成员内部类(static修饰)
1>静态资源访问时不需要创建对象,可以通过类名直接访问
2>访问静态类中的静态资源可以通过"…"链式加载的方式访问
package cn.tedu.innerclass;
/**
* 本类用来测试成员内部类被static修饰
*/
public class TestInner3 {
public static void main(String[] args) {
//4.创建内部类对象
//方式1:创建内部类对象调用show()
// Outer3.Inter3 oi=new Outer3().new Inter3();
// oi.show();
//方式2:创建内部类匿名对象访问show()
// new Outer3().new Inter3().show();
/**
* 现象:当内部类被static修饰以后,new Outer3报错
*/
//6.用static修饰内部类以后,上面的创建语句报错
Outer3.Inter3 oi=new Outer3.Inter3();
oi.show();
//7.匿名内部类对象调用show()
new Outer3.Inter3().show();
//9.访问静态内部类中的静态资源---链式加载
/**
* 没有创建任何一个对象,直接都是通过类名找到的静态资源
* 像这样连着点的方式,就是:链式加载
*/
Outer3.Inter3.show2();
}
}
//1.创建外部类
class Outer3{
//2.创建成员内部类
//5.内部类被static修饰---不常用,浪费内存
static class Inter3{
//3.定义成员内部类中的普通成员方法
public void show(){
System.out.println("Inner...show()");
}
//8.定义成员内部类的静态成员方法
static public void show2(){
System.out.println("Inner...show2()");
}
}
}
2.5 局部内部类
package cn.tedu.innerclass;
/**
* 本类用于测试局部内部类
*/
public class TestInner4 {
public static void main(String[] args) {
//5.创建外部类对象调用
new Outer4().show();
/**
* 如何使用局部内部类的资源呢?
* 创建外部类对象调用外部方法或者在main()创建局部内部类的对象都是不可行的
* 需要在外部类中创建内部类的对象,并且调用内部类的方法,才会触发内部类的功能
*/
}
}
//1.创建外部类
class Outer4{
//2.创建外部类的成员方法
public void show(){
System.out.println("Outer4...show()");
//3.创建局部内部类
class Inter4{
//4.创建局部内部类的属性与方法
String name;
int age;
public void eat(){
System.out.println("Inter4...eat()");
}
}
/**
* 如何使用局部内部类的资源?
* 在show方法里,创建内部类对象,调用内部类的功能
*/
Inter4 i=new Inter4();
i.eat();
System.out.println(i.name);
System.out.println(i.age);
}
}
2.6 匿名内部类
package cn.tedu.innerclass;
/**
* 本类用于测试匿名内部类
* 匿名内部类没有名字,通常和匿名对象结合在一起使用
*/
public class TestInner5 {
public static void main(String[] args) {
//3.创建接口1对应的匿名对象与匿名内部类
new Inter(){
@Override
public void save() {
System.out.println("save");
}
@Override
public void get() {
System.out.println("get");
}
}.get();
//5.创建抽象类对应的匿名对象与匿名内部类
new Inter2() {
@Override
public void drink() {
System.out.println("酒");
}
}.drink();
//7.调用普通类的功能(创建匿名对象,直接调用)
new Inter3().power();
new Inter3().power();//new了两次,是两个匿名对象
/**
* 如果想要多次使用实现后的功能,还是要创建普通的对象
* 匿名对象只能使用一次,一次只能调用一个功能
* 匿名内部类其实就充当了实现类的角色,去实现未实现的抽象方法,只是没有名字而已
*/
Inter3 i3=new Inter3();
i3.power();
i3.study();
}
}
//1.创建接口
interface Inter{
//2.定义接口中的抽象方法
void save();
void get();
// default void eat(){
// System.out.println("我是默认方法");
// }
}
//4.创建抽象类
abstract class Inter2{
public void play(){
System.out.println("inter2...play");
}
abstract public void drink();
}
//6.创建普通类
class Inter3{
public void power(){
System.out.println("锵锵锵");
}
public void study(){
System.out.println("嘻嘻嘻");
}
}