文章目录
1多态
1.1多态概述
多态:不同时刻表现出不同的形态,比如平常生活中,猫可以表现动物的形态,也可以表现猫的特定形态。
多态的前提:
1、有继承或者实现关系
2、方法的重写
3、父类的引用指向子类的对象
1.2多态例子解析
class Animal {
public void eat(){
System.out.println("动物吃饭");
}
}
//满足多态继承前提
class Cat extends Animal {
//满足多态前提方法重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test1Polymorphic {
public static void main(String[] args) {
// 当前事物, 是一只猫
Cat c = new Cat();
// 当前事物, 是一只动物,不同形态体现多态
//满足多态前提父类的引用指向子类对象
Animal a = new Cat();
a.eat();
}
}
1.3多态中成员的访问特点
- 成员变量:编译看父类,运行看父类
- 成员方法:编译看父类,运行看子类
1.4多态的好处和弊病
- 好处
提高程序的扩展性。定义方法的时候,使用父类型作为参数,在使用具体的子类参与操作(成员方法的访问特点) - 弊病
不能使用子类的特有成员
1.5多态中的转型
1、向上转型的格式:多态的格式,父类引用指向子类对象
2、向下转型的格式:子类型 对象名 = (子类型)父类引用
为了解决多态不能获取子类的特有成员,我们可以考虑:
- 直接使用子类来创建对象
- 使用向下转型
- 通过分析可以得出结论,多态用于共享的成员方法中,在私有的成员方法使用直接创建对象或向下转型
1.6多态中转型存在的风险
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
异常。为了防止此风险,子类对象需要添加instanceof关键字来判断条件是否成立,再来向下转型和执行里面的特有方法。
1、instanceof关键字格式:变量名 instanceof 类型
2、理解:判断关键字左边的变量是否是右边的类型,是返回true,否则返回false
1.7多态向下转型例子解析
abstract class Animal {
public abstract void eat();
}
class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test4Polymorpic {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ // Animal a = new Dog();
// Animal a = new Cat();
a.eat();
//a.watchHome();
// Dog dog = (Dog) a;
// dog.watchHome(); // ClassCastException 类型转换异常
// 判断a变量记录的类型, 是否是Dog
if(a instanceof Dog){
Dog dog = (Dog) a;
dog.watchHome();
}
}
}
2内部类
在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
2.1内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
2.2内部类的分类
2.21成员内部类
2.211普通成员内部类
成员内部类所在位置为成员的位置,由此称为成员内部类
- 外界创建成员内部类的格式:外部类名.内部类名 对象名 = 外部类对象.内部对象
- 举栗子,Outer.Inner oi = new Outer().new Inner();
2.212私有成员内部类
私有成员内部类将一个类设计为内部类的目的,大多数是不想让外界去访问,所以内部类的定义应该私有化,私有化后,在提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
class Outer {
private int num = 20;
private class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
public class InnerDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
2.212静态成员内部类
- 静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部对象名.内部类名();
- 静态成员内部类中的静态方法访问格式:外部类名.内部类.方法名();
class Outer {
static class Inner {
public void show(){
System.out.println("inner..show");
}
public static void method(){
System.out.println("inner..method");
}
}
}
public class Test3Innerclass {
/*
静态成员内部类演示
*/
public static void main(String[] args) {
// 外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
oi.show();
Outer.Inner.method();
}
}
2.23局部内部类
局部内部类定义位置处在方法内部中定义的类,我们之前称方法内部的变量为局部变量,故此同理,方法里面的类称为局部内部类。
- 局部类部类访问方式
- 局部内部类,外界是无法直接使用的,成员方法的生命周期会随方法的调用完毕也结束,在外界创建局部内部类就会显得没有意义,因此需要在方法内部创建对象并使用。
- 该类可以直接访问外部类成员,也可以访问该方法内的局部变量
class Outer {
private int num = 10;
public void method() {
int num2 = 20;
class Inner {
public void show() {
System.out.println(num);
System.out.println(num2);
}
}
Inner i = new Inner();
i.show();
}
}
public class OuterDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
2.24匿名内部类
2.241匿名内部类概述
匿名内部类是特殊的局部内部类,实际上是接口的实现类或继承该类的子类对象,由于这个对象没有名称,故此称为匿名内部类。
- 匿名内部类的前提
- 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
- 匿名内部类的格式
- 格式1:new 类名(){重写方法}
- 格式2:new 接口名(){重写方法}
new Inter(){
@Override
public void method(){}
}
匿名内部类是将创建对象、继承\实现、方法重写三个步骤放在了一起,可以便捷的创建子类对象
2.243匿名内部类调用方式
- 匿名内部类可以通过多态的形式接收再调用
Inter i = new Inter(){
@Override
public void method(){
}
}
- 匿名内部类可以直接调用
直接调用只能调用一个重写方法方法
当我们想调用两个以上重写的方法时,就可以使用多态的形式接收,再分别调用即可。
interface Inter{
void method();
}
class Test{
public static void main(String[] args){
new Inter(){
@Override
public void method(){
System.out.println("我是匿名内部类");
}
}.method(); // 直接调用方法
}
}
2.244匿名内部类的应用
- 匿名内部类在开发中的使用
- 当发现某个方法需要,接口或抽象类的子类对象,我们就可以将匿名内部类作为参数传递过去,来简化传统的代码
/*
游泳接口
*/
interface Swimming {
void swim();
}
public class TestSwimming {
public static void main(String[] args) {
goSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("铁汁, 我们去游泳吧");
}
});
}
/**
* 使用接口的方法
*/
public static void goSwimming(Swimming swimming){
/*
Swimming swim = new Swimming() {
@Override
public void swim() {
System.out.println("铁汁, 我们去游泳吧");
}
}
*/
swimming.swim();
}
}
3lambda
3.1lambda表达式概述
lambda表达式可以理解为对匿名内部类的优化,当然这里面的优化是有前提的,即lambda表达式的使用前提
1、有一个接口
2、接口中有且仅有一个抽象方法
lambda思想:
lambda表达式是采用函数式编程思想,而匿名内部类采用面向对象编程思想。在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”,而我们要学习的Lambda表达式就是函数式思想的体现
3.2lambda表达式格式
(形式参数)->{代码块}
- 形式参数如果只有一个,小括号可以省略
- 形式参数的数据类型可以省略,如果两个以上的形式参数如果要省略数据类型,必须一起省略,否则只省略一个会编译错误。
- 代码块只有一行语句,大括号、封号,甚至有return都可以一起省略
组成Lambda表达式的三要素:
- 形式参数,箭头,代码块
3.3Lambda表达式和匿名内部类的区别
- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件在硬盘中
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成,在内存中存储,不会存硬盘中。