目录
一:抽象类
* 概念:
* 什么叫抽象类: 只要一个类 是abstract修饰的那么这个类就是抽象类
* 和 这个类中有没有抽象方法,没有必然的联系
*
* 什么叫抽象方法:
* 方法由abstract修饰,并且没有方法体的方法
*
* 一个抽象类 是可以没有抽象方法;因为可能有抽象方法,所以这个类的作用不允许你创建对象(实例化)。
* 作用:
* 不能创建对象,天生作为父类
* 抽象类中的抽象方法有什么作用?
* 1.抽象类中的抽象方法不是为了调用的
* 2.为了强制子类去重写
二 .接口
1.接口的概念
* 接口是功能的集合,就是方法的集合
* 接口中只能定义方法,不能定义普通的成员变量
*
* 而且接口中的成员方法,必须都是抽象的
* 问题:接口能创建对象吗? 不能
* 接口有什么用? 天生作为"父接口"
2.接口的定义
* 定义类用关键字:class
* 定义抽象类用关键字:abstract class
* 定义接口用关键字:interface
*
* 格式:
* public interface 接口名字{
* //成员变量,不能定义普通的成员变量
* //成员方法:必须都是抽象
* public abstract 返回值类型 方法名();
* }
4.各种类型之间的关系
* a.类和类之间:继承,而且是单继承,一个子类 只能有一个直接父类
* b.接口和接口之间:继承,但是可以多继承,一个子接口 可以有多个直接父接口
*
* 面试题:java到底支不支持多继承?
* 如果是类与类 不支持多继承,只支持多层继承
* 如果是接口和接口 支持多继承,也支持多层继承
* c.类和接口之间: 不叫继承(extends),叫实现(implements),可以多实现
* 只有 类 实现 接口5.接口中成员的特点
* 成员变量,但是必须有固定修饰符 public static final 数据类型 变量= 值
* 成员方法,必须是固定修饰符 public abstract 即 抽象方法ps:固定修饰符 我们可以不写,但是不写不代表没有,编译器自动添加。
* 接口不可以创建对象(抽象类也是)
* 实现类 以及 实现类接口 ,那么必须重写接口中所有的抽象方法,然后才能创建对象
* 否则 这个实现类 还是一个抽象类,是不能创建对象的
三:接口和抽象类的异同
1.相同点:
* a.都不能创建对象
* b.都是作为父类/父接口
* c.子类/实现类 都必须重写抽象方法,然后才能创建对象
2.不同点:
* a.抽象类用关键字 abstract 接口用关键字interface
* b.抽象类中可以有抽象方法,可以没有抽象方法,也可以有部分是抽象方法,部分不是抽象方法
* 接口中只要是方法,必须都是抽象的
* c.抽象类可以定义任意成员变量 接口的成员变量必须public static final修饰
* d.类和抽象类之间关系是单继承, 类和接口之间关系是多实现
* e.思想上的区别
* 1.抽象类中 必须定义 整个继承体系的共性内容ps:继承遵循向上抽取原则,父类代表子类的共性。
* 2.接口中定义 整个继承体系之外的 额外的 扩展的功能
四:多态
指对象的多态
* 一个对象 的多种状态
* 比如: 一只狗 是狗,是动物,是生物
* 比如: 一个人 是人,是动物,是生物
* 比如: 你 在学校是学生,在家里是儿子,在公司员工,在外面老大ps:通过父类引用指向子类对象,不同的子类可以呈现出不同的状态,就叫多态。
在java中的多态
* 前提:
* 1.必须有子父类关系 (必须有继承)
* 2.必须有方法的重写
* 多态在java中表现形式:
* 父类类型 变量名 = new 子类类型();
* 父类类型的变量 指向了 子类的对象ps:多态的前提,有继承,有方法重写,有父类引用指向子类对象。
ps:父类引用可以是抽象父类,也可以是父接口。
静态多态:指在编译阶段出现的静态多态,通过方法的重载来达到。
动态多态:指在运行阶段出现的动态多态,通过方法的重写来达到。
注意事项:
* 使用多态调用成员变量
* 编译时 看父类 看左边
* 运行时 看父类 看左边
* 使用多态调用成员方法
* 编译时 看父类 看左边
* 运行时 看子类 看右边
*
ps:多态使用变量 编译运行都看父类,多态调用方法 编译看父类 运行看子类。多态的弊端:
* 多态只能调用子父类和子类共有的方法,不能调用子类特有的方法。多态弊端的解决方法:
* 向上向下转型:
* 向上转型: 多态
* 向下转型: 子类类型 变量名 =(子类类型)父类类型的变量;
* 变量名.子类特有方法();
ps:直接 向下转型过程中 很容易出现 类型转换的异常
* 关键字:运算符 instanceof
* 判断某一个对象 是不是属于一个类
* 格式:
* boolean b = 变量名 instanceof 类名
五:代理
动态语言:不需要编译,在运行阶段确定数据类型;静态语言,需要编译,在编译的时候就能确定数据类型。
反射:是在运行阶段可以获取整个类的方法,属性,构造函数。
获取Class对象:Class clazz=Class.forName("类的全路径"); (最常用)
创建对象的两种方法:
第一种:
Class clazz=Class.forName("reflection.Person");默认需要有空的构造函数。
第二种:
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class); //创建对象并设置属性
Person p1=(Person) c.newInstance("李四","男",20);反射的应用场景:如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时的信息来发现该对象和类的真实信息,此时就必须使用到反射了。Person p=new Student();其中编译时类型为Person,运行时类型为Student。
反射总结:
java从反射角度来说属于动态语言,所以说反射就是慢,在运行时候才能确定所属类,但是解决了编译时候无法确定所属类的问题。
代理分为静态代理和动态代理。代理就是使用实现类的方法把调用统一起来。是对实现类的一个封装。
1.创建接口。
2.创建多个实现类。
3.创建一个代理。(类内部有接口对实现类的多态形式)
静态代理:自己手动编写代理类。
第一步: 1 package ceshi1; 2 public interface Iuser { 3 void eat(String s); 4 }
第二步:
1 package ceshi1; 2 public class UserImpl implements Iuser { 3 @Override 4 public void eat(String s) { 5 System.out.println("我要吃"+s); 6 } 7 }
第三步:
1 package ceshi1; 2 public class UserProxy implements Iuser { 3 private Iuser user = new UserImpl(); 4 @Override 5 public void eat(String s) { 6 System.out.println("静态代理前置内容"); 7 user.eat(s); 8 System.out.println("静态代理后置内容"); 9 } 10 }
动态代理:自动生成代理类。
// 要被增强的对象 final NormalPerson p=new NormalPerson(); // 需求: 使用动态代理的方式对普通人进行增强 // jdk提供的类和方法可以给咱们动态的生成代理对象/增强对象 不需要自己去创建增强类 Person proxyPerson =(Person)Proxy.newProxyInstance( p.getClass().getClassLoader(), p.getClass().getInterfaces(), new InvocationHandler() { /*参数概述 固定死的 * 参数2: 要被增强的方法 * */ @Override //invoke里面是所有的增强业务逻辑代码 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("eat".equals(method.getName())) { // 参数1:本身应该执行这个方法的对象 method.invoke(p, args); System.out.println("增强了:变成飞..."); return "abcd"; } return method.invoke(p, args); } }); proxyPerson.run(); // 执行这个方法 invoke都会执行一遍 执行的内容就是针对该方法的增强
总结:
静态代理:需要手动去实现代理类,非常不灵活。
动态代理:不用手动实现代理类,通过JDK或者cglib自动生成代理对象。
ps:代理类似于装饰者模式的思想。
JDK动态代理:必须要实现了接口的业务类才能用这种办法生成代理对象。新版本也开始结合ASM机制(利用字节码操作机制)。
cglib动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。