调用

同步调用

1
2
3
4
5
6
7
8
9
10
11
12
public class Person{
public void openDoor(Door door){
door.open();
System.out.println("新的一天开始了");
}

}
class Door{
public void open(){
System.out,println("门被打开了");
}
}

在打印“新的一天开始了”内容时,door.open()方法已经执行结束。(可能会造成阻塞问题)

异步调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person{
public void openDoor(Door door){
new Thread(new Runnable(){
public void run(){
door.open();
}
}).start();
System.out.println("新的一天开始了");
}

}
class Door{
public void open(){
System.out,println("门被打开了");
}
}

将调用放在一个新的线程中,后边的操作将不再依赖于调用方法的结束。(解决同步调用的阻塞问题)

回调

双向调用模式

A callback is a function this is passed as an argument to another function and is executed after its patent function has completed.

回调是一个函数,它作为参数传递给另一个函数,并在其父函数完成后执行

回调的思想是:一.类A的a()方法调用类B的b()方法

二.类B的b()方法执行完毕主动调用类A的callback()方法

原理:首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象,控制器对象负责检查某个场景是否出现或某个条件是否满足,当满足时,自动调用回调对象的方法。

例一:

情景:老板A对员工B说,我现在交给你一个任务,并且我把我的电话号码给你,你一旦完成任务就给我打电话。

1.创建一个回调接口

1
2
3
4
public interface CallBack
{
public void acceptEvent();
}

2.创建回调接口的实现类,即本例中的老板类

1
2
3
4
5
6
7
public class Boss implements CallBack
{
public void acceptEvent()
{
System.out.println("打电话给老板,告知已经完成工作了");
}
}

3.创建控制类,也就是本例中的员工对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Employee
{
CallBack callBack;
public Employee(CallBack callBack)
{
this.callBack=callBack;
}
public void doWork()
{
System.out.println("工作中....");
callBack.acceptEvent(); //接口实现类的对象调用接口中的方法
}
}

创建测试类

1
2
3
4
5
6
7
8
9
10
public class TestMain
{
public static void main(String[] args)
{
//创建控制器对象,将提供给他的回调对象传入
Employee employee=new Employee(new Boss()); //传递一个实现接口的类的对象
//启动控制器对象运行
employee.doWork();
}
}

在本例中,如果员工的工作结果可能不止向一类人传递,比如还会给经理、测试等人传递结果。这时我们可以将接收结果这个方法看做一种公共需求,接收到结果后后续处理操作也不一样,我们可以将这个方法定义到接口中,再在具体的类中进行实现。

例二:(安卓中的实例)

在android中回调机制被大量的使用。比如,在Activity中定义了很多生命周期的不同状态要调用的方法,这些方法都是空实现,系统框架要调用,用户也要调用来实现。

  举个简单的例子就是Button的点击响应事件实现机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import android.app.Activity;  
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

/**
* 这个就相当于Class A
* 实现了 OnClickListener接口
*/
public class MainActivity extends Activity implements OnClickListener{
/**
* Class A 包含Class B(View)的引用
*/
private Button button;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button1);

/**
* Class A 调用View的方法,而Button extends View----->A类调用B类的某个方法 b
*/
button.setOnClickListener(this);
}

/**
* 用户点击Button时调用的回调函数,你可以做你要做的事
*/
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "OnClick", Toast.LENGTH_LONG).show();
}

}

例三:(还是安卓实例)

点击文本框弹出对话框,将输入的文字显示在文本框中。

https://github.com/liuyuxin-cloud/Android/tree/main/callbackdemo

反射

原理

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

反射机制使java具有动态特性

理解

关于java.lang.Class 类的理解

​ 将类编译生成字节码文件(.class)再用(java.exe)命令对每个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。

加载到内存中的类称为运行时类,它们是Class类的实例。

(java万物皆对象 我们的自定义类是Class类的对象)

用途

1、反编译:.class–>.java

2、通过反射机制访问java对象的属性,方法,构造方法等

3、当我们在使用IDE,比如Ecplise,IntelliJ IDEA时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。

例子:

反射前对于自定义类的操作:

1.通过构造器实例化对象

2.通过对象调用内部属性、方法

3.在类外部不可以通过对象调用内部私有(private)结构(封装性的限制)

反射后:

1.通过反射创建类的对象

2.通过反射调用对象指定的属性、方法

3.通过反射可调用类的私有结构

什么时候使用反射?

1.具有动态性时

2.编译时不确定造哪个对象时

常用类

Java.lang.Class;

Java.lang.reflect.Constructor;

Java.lang.reflect.Field;

Java.lang.reflect.Method;

Java.lang.reflect.Modifier;

基本使用

1、获得Class:主要有三种方法:

(1)Object–>getClass

(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

(3)通过class类的静态方法:forName(String className)(最常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
//第一种方式获取Class对象  
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象

//第二种方式获取Class对象
Class stuClass2 = Student.class;

//第三种方式获取Class对象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

2、判断是否为某个类的示例:

一般的,我们使用instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断时候为某个类的实例,他是一个native方法。

1
public native boolean isInstance(Object obj);

3、创建实例:通过反射来生成对象主要有两种方法:

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

1
Class<?> c = String.class;Object str = c.newInstance();

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

1
2
3
4
5
//获取String的Class对象
Class<?> str = String.class;//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=c.getConstructor(String.class);//通过参数判断指定的构造器
//根据构造器创建实例:
Object obj = constructor.newInstance(“hello reflection”);

4、通过反射获取构造方法并使用:

(1)批量获取的方法:
public Constructor[] getConstructors():所有”公有的”构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

(2)单个获取的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的”公有的”构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取”某个构造方法”可以是私有的,或受保护、默认、公有;

https://blog.csdn.net/a745233700/article/details/82893076