Day19
反射获取方法
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)
@Test
public void getFunction() throws Exception {
//1.获取目标资源的字节码对象--本方法的参数是类的全路径名,包名.类名
Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
//2.获取所有成员方法
Method[] ms = clazz.getMethods();
//3.遍历数组,获取每个方法的信息
for(Method m : ms){
System.out.println(m.getName());//获取方法名
Class<?>[] pt = m.getParameterTypes();//获取本方法的参数类型
System.out.println(Arrays.toString(pt));//打印查看这些参数类型
}
}
反射获取成员变量
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
/注意!!!目前成员变量的修饰符必须是public才能获取到
* 采用默认的修饰符反射获取不到/
//5.通过单元测试获取成员变量
@Test
public void getFields(){
//1.获取目标资源对应的字节码对象
Class<?> clazz = Student.class;
//2.获取所有成员变量
/*注意!!!目前成员变量的修饰符必须是public才能获取到
* 采用默认的修饰符反射获取不到*/
Field[] fs = clazz.getFields();
//3.遍历数组,获取每个成员变量对应的信息
for (Field f: fs){
System.out.println(f.getName());//获取参数名
System.out.println(f.getType().getName());//获取变量类型 名
}
}
反射新建实例对象
c.newInstance();//执行无参构造创建对象
c.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法
/方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象
* 方式二:先获取指定的构造函数,再通过这个构造函数对象来创建对象/
@Test
public void getObject() throws Exception {
//1.获取目标资源对应的字节码对象
Class<?> clazz = Student.class;
//2.创建对象
Object obj = clazz.newInstance();/*会触发无参构造*/
//需要重写Student中的toString(),否则打印的是对象的地址值
System.out.println(obj);//Student{name='null', age=0}
System.out.println("下面的内容是方式二");
//3.要指定去调用哪个构造方法来创建对象,所以要先获取这个指定的构造方法
/*注意!!!这个方法参数类型是构造方法参数类型对应的字节码对象*/
Constructor<?> c = clazz.getConstructor(String.class, int.class);
Object obj2 = c.newInstance("张三", 3);
System.out.println(obj2);
//5.通过反射创建出来的对象获取属性值并使用方法
/*注意!此处需要把父类型Object类型的obj强制转换成子类型Student的s
* 为什么要强转?因为此处我们想要使用子类的特有功能,而父类无法使用子类的特有功能
* Object父类中是没有Student子类自己的属性与功能的
* 所以,我们把之前看做是父类型的子类对象转回成子类型,去调用子类的特有功能
* 这个现象称作:向下造型
* 【注意!!!向下造型之前必须先向上造型,纯纯的父类对象不能向下造型】*/
Student s = (Student)obj2;
System.out.println(s.name);
System.out.println(s.age);
s.play(666);
}
//5.通过反射创建出来的对象获取属性值并使用方法
/注意!此处需要把父类型Object类型的obj强制转换成子类型Student的s
* 为什么要强转?因为此处我们想要使用子类的特有功能,而父类无法使用子类的特有功能
* Object父类中是没有Student子类自己的属性与功能的
* 所以,我们把之前看做是父类型的子类对象转回成子类型,去调用子类的特有功能
* 这个现象称作:向下造型
* 【注意!!!向下造型之前必须先向上造型,纯纯的父类对象不能向下造型】/
*
暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源, 方法格式以 "getDeclared----"开头
//单元测试方法的格式:@Test+public+void+无参
/单元测试 1:暴力反射获取与设置私有属性值/
----------暴力反射!!!注意!!!要设置私有可见,不然访问不了
field.setAccessible(true);
package cn.tedu.reflection;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/*本类用于测试暴力反射*/
public class TestReflect2 {
//单元测试方法的格式:@Test+public+void+无参
/*单元测试 1:暴力反射获取与设置私有属性值*/
@Test
public void getFields() throws Exception {
//1.获取目标资源对应的字节码对象
Class<?> clazz = Person.class;
//2.获取指定名称的私有属性
Field field = clazz.getDeclaredField("name");
//3.根据获取到的属性对象拿到它的类型
System.out.println(field.getType().getName());
System.out.println(field.getType());
//4.设置属性的值
//4.1 没有对象就通过反射的方式创建对象
Object obj = clazz.newInstance();
//4.2 暴力反射!!!注意!!!要设置私有可见,不然访问不了
field.setAccessible(true);
//4.3刚刚获取到的name属性对象设置值
/*注意需要设置两个参数:哪个对象的属性值,以及要设置一个什么值
* set(m,n) m-给哪个对象设置值 n-给这个对象的属性设置一个什么值*/
field.set(obj,"林冲");
//4.4打印刚刚给属性设置的值
/*注意还是需要指定获取的是哪个对象的这个属性的值*/
System.out.println(field.get(obj));
}
}
/单元测试 2 :通过暴力反射获取与使用方法/
//2.通过暴力反射获取私有方法
/本方法的参数列表是
1.getDeclaredMethod(name,x,y,z…)
* name:指的是要获取方法的名字
* x,y,z…可变参数,指的是要获取方法的参数类型,注意是字节码对象“.class”
*
2./invoke()用来调用目标方法,参数1是执行哪个对象的这个方法
后续的参数是执行目标方法时传入的参数,这个参数是可变参数,根据目标方法的具体情况来写/
/*单元测试 2 :通过暴力反射获取与使用方法*/
@Test
public void getFunction() throws Exception {
//1.获取目标资源对应的字节码对象
Class<?> clazz = Person.class;
//2.通过暴力反射获取私有方法
/*本方法的参数列表是getDeclaredMethod(name,x,y,z...)
* name:指的是要获取方法的名字
* x,y,z...可变参数,指的是要获取方法的参数类型,注意是字节码对象“.class”*/
Method method = clazz.getDeclaredMethod("add", String.class, int.class);
//3.1没有对象就通过反射创建对象
Object obj = clazz.newInstance();
//3.2如果想要执行私有的方法,也要设置私有可见
method.setAccessible(true);
//3.3执行获取到的这个私有方法
/*invoke()用来调用目标方法,参数1是执行哪个对象的这个方法
后续的参数是执行目标方法时传入的参数,这个参数是可变参数,根据目标方法的具体情况来写*/
method.invoke(obj,"李四儿",18);
}
method.invoke(obj,“李四儿”,18);//获取并设置私有方法,自打印
内部类
如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。
就是把类定义在类的内部的情况就可以形成内部类的形式。
- 内部类可以直接访问外部类中的成员,包括私有成员
- 外部类要访问内部类的成员,必须要建立内部类的对象
- 在成员位置的内部类是成员内部类
- 在局部位置的内部类是局部内部类
测试1: 成员内部类(类里方法外)*/*
package cn.tedu.innerclass;
/*本类用于测试内部类的入门案例*/
public class TestInner {
public static void main(String[] args) {
//创建内部类对象, 外部类名.内部类名 对象名 = new 外部类名().new内部类名()
Outer.Inner oi = new Outer().new Inner();
oi.delete();
System.out.println(oi.sum);
//调用外部类的方法--这样创建的是一个匿名的外部类对象,只使用一次
new Outer().find();
}
}
class Outer{
String name;
private int age;
public void find(){
System.out.println("Outer...find()");
//System.out.println(sum);不能直接使用内部类的属性
//delete();不能直接使用内部类的方法
/*外部类想用内部类资源,比逊先创建内部类对象
* 通过内部类对象来调用内部类的资源*/
Inner in = new Inner();
System.out.println(in.sum);
in.delete();
}
//2.创建内部类Inner--外部类的一个特殊成员
/*根据内部类的位置的不同,分为成员内部类(类里方法外)
* 局部内部类(方法类)*/
class Inner{
int sum = 10;
public void delete(){
System.out.println("Inner...delete");
//测试内部类可以直接使用外部类的资源,也包含私有资源
System.out.println(name);
System.out.println(age);
//find();
/*注意此处测试, 否则来回调用会抛出栈溢出异常StackOverflowError*/
}
}
}
测试2://私有成员内部类被包含在外部类之中,被看做是外部类
package cn.tedu.innerclass;
public class TestInner2 {
public static void main(String[] args) {
//new Outer2().new Inner2();外部无法直接创建内部类private
//创建外部类对象间接访问私有内部类的资源
new Outer2().getInner2Eat();
}
}
class Outer2{
//私有内部类被包含在外部类之中,被看做是外部类
public void getInner2Eat(){
Inner2 in = new Inner2();
in.eat();
}
private class Inner2{
public void eat(){
System.out.println("chichi");
}
}
}
测试3://成员内部类为静态类
- 6.可以直接外部类类名创建内部类对象*/—只可以访问一次
package cn.tedu.innerclass;
public class TestInner3 {
public static void main(String[] args) {
// Outer3.Inner3 oi = new Outer3().new Inner3();
// oi.show();
// //创建匿名对象的方式来访问show()--只可以访问一次
// new Outer3().new Inner3().show();
//
/*现象:内部类被static修饰以后 报错
* 6.可以直接外部类类名创建内部类对象*/
Outer3.Inner3 oi = new Outer3.Inner3();
oi.show();
/*访问静态内部类中的静态资源可以链式加载*/
new Outer3.Inner3().show();
}
}
class Outer3{
//内部类为静态类
static class Inner3{
public void show(){
System.out.println("Inne3....show()");
}
//改为静态方法
static public void show2(){
System.out.println("Inne3....show2()");
}
}
}
测试4://局部内部类(方法里):
package cn.tedu.innerclass;
//局部内部类
public class TestInner4 {
public static void main(String[] args) {
new Outer4().show();
}
}
class Outer4{
public void show(){
class Inner4{
String name;
int age;
public void eat(){
System.out.println("Inner4''''eat()");
}
}
/* 在show()创建内部类对象 */
Inner4 in = new Inner4();
in.eat();
System.out.println(in.age);
System.out.println(in.name);
}
}
测试5:匿名内部类:
//创建匿名内部类
/以前使用接口,需要先创建接口的实现类
* 接着接口实现类需要添加接口中未实现的方法/
/相当于创建接口实现类+重写接口中的方法+创建对象+调用功能/
package cn.tedu.innerclass;
/*测试匿名内部类
* */
public class TestInner5 {
public static void main(String[] args) {
//创建匿名内部类
/*以前使用接口,需要先创建接口的实现类
* 接着接口实现类需要添加接口中未实现的方法并实现
//最后需要创建接口实现类对象并进行调用,这样比较复杂*/
//如果有的方法只是需要使用一次,就没必要这么复杂,使用匿名内部类即可
/*相当于创建接口实现类+重写接口中的方法+创建对象+调用功能*/
new Inner1(){
@Override
public void save() {
System.out.println("Inner1 .... save()");
}
@Override
public void get() {
System.out.println("Inner1....get()");
}
}.get();//触发实现get(),注意只能使用一个,使用一次
new Inner2() {
@Override
public void drink() {
System.out.println("hedianba");
}
}.drink();
new Inner3().power();
Inner3 i3 = new Inner3();
i3.power();
i3.study();
}
}
//1.创建接口
interface Inner1{
void save();
void get();
}
//抽象类
abstract class Inner2{
public void play(){
System.out.println("Inner2...play()");
}
abstract public void drink();
}
//3.普通类
class Inner3{
public void study(){
System.out.println("学习");
}
public void power(){
System.out.println("光头强");
}
}