转载请注明出处:https://blog.csdn.net/jiyisuifeng222/article/details/117710305
本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 情花打雪 即可关注,每个工作日都有文章更新。
本篇文章主要讲解内容如下:
- 向上转型和向下转型介绍
- 类和接口的区别
- 静态获取接口的实例:接口的实现类
- 动态获取接口的实例:动态代理
- 动态代理的原理
1.向上转型和向下转型介绍
父子对象之间的转换分为了向上转型和向下转型,它们区别如下:
- 向上转型 : 通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换
- 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换
1-1.向上转型
向上转型示例:
class Person {
public void print() {
System.out.println("person:print");
}
}
class Lisi extends Person {
public void print() {
System.out.println("lisi:print");
}
}
public class TestDemo{
public static void main(String args[]){
Person p = new Lisi();//通过子类去实例化父类
p.print();
}
}
运行结果:lisi:print
可以看到运行结果的是class Lisi的print,这是因为我们通过子类Lisi去实例化的,所以父类Person的print方法已经被子类Lisi的print方法覆盖了.从而打印class Lisi的print.
注意:向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.
1-2.向下转型
在java中,向下转型是为了通过父类强制转换为子类,从而来调用子类特有的方法
为了保证向下转型的顺利完成,在java中提供了一个关键字:instanceof,通过instanceof可以判断某对象是否是某类的实例,如果是则返回true,否则为false,
instanceof使用如下:
A a = new B(); //向上转型 (B类是A的子类)
a instanceof A; //返回true.
a instanceof B; //返回true
a instanceof C; //返回false
向下转型示例:
class A {
public void print() {
System.out.println("A:print");
}
}
class B extends A {
public void print() {
System.out.println("B:print");
}
public void funcB() {
System.out.println("funcB");
}
}
class C extends A {
public void print() {
System.out.println("C:print");
}
public void funcC() {
System.out.println("funcC");
}
}
public class Test {
public static void func(A a) {
a.print();
if (a instanceof B) {
B b = (B) a;//向下转型,通过父类实例化子类
b.funcB();//调用B类独有的方法
} else if (a instanceof C) {
C c = (C) a;//向下转型,通过父类实例化子类
c.funcC();//调用C类独有的方法
}
}
public static void main(String args[]) {
func(new A());
func(new B());
func(new C());
}
}
运行结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/bin/java/Users/JavaSeDemo/JavaSeDemo demo.TestJavase
A:print
B:print
funcB
C:print
funcC
通过向下转型来调用B类和C类独有的方法.
2.类和接口的区别
Java中class
和interface
的区别:
- 可以实例化
class
(非abstract
类); - 不能实例化
interface
。
所有interface
类型的变量总是通过向上转型并指向某个实例的:
CharSequence cs = new StringBuilder();
3.静态获取接口的实例:接口的实现类
传统编码方式流程如下:
首先定义接口:
public interface Hello {
void morning(String name);
}
然后编写实现类:
public class HelloImpl implements Hello {
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
最后创建实例,转型为接口并调用:
Hello hello = new HelloImpl();
hello.morning("Bob");
4.动态获取接口的实例:动态代理
目的:(不人为编写实现类的前提下)为了获得接口的实例,并实现接口中的方法,并调用该方法。
public class Main {
public static void main(String[] args) {
//1.创建一个handler。它负责接口中的方法实现
InvocationHandler handler = new InvocationHandler() {
//invoke函数参数说明:代理对象;想调用的方法;方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
//2.创建一个Proxy的instance。需要传入三个参数:接口的类加载器;要实现的接口;调用方法的handler
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[]{Hello.class}, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
在运行期动态创建一个interface实例的方法如下:
- 定义一个
InvocationHandler
实例,它负责实现接口的方法调用; - 通过
Proxy.newProxyInstance()
创建interface
实例,它需要3个参数:
** 使用的ClassLoader
,通常就是接口类的ClassLoader
;
** 需要实现的接口数组,至少需要传入一个接口进去;
** 用来处理接口方法调用的InvocationHandler
实例。 - 将返回的
Object
强制转型为接口。
动态代理实际上是JDK在运行期动态创建class字节码并加载的过程.
把上面的动态代理运行过程还原为静态实现类大概是这样:
public class HelloStaticImpl implements Hello {
InvocationHandler handler;
public HelloDynamicProxy(InvocationHandler handler) {
this.handler = handler;
}
public void morning(String name) {
handler.invoke(//由handler去调用函数
this,//Proxy对象
Hello.class.getMethod("morning"),//方法名
new Object[] { name });//方法参数
}
}
通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用(invoke方法可能会调用多次,需要根据第二个参数Method判断业务做相应处理)
关注我的技术公众号,每天都有优质技术文章推送。
微信扫一扫下方二维码即可关注: