Java笔记--多态

        “我曾经被问到‘求教,Baddage先生,如果你向机器中输入错误的数字,可以得到正确的答案吗?’ 我无法恰当地理解产生这种问题的概念上的混淆。”

                                                                                          --Charles Babbage(1791-1871)

1、多态的概述 

        多态方法的调用允许一种类型表现处与其他相似类型之间的区别,只要他们是从同一个基类中导出而来的。这种区别是根据方法行为的不同而表现出来的,虽然这些方法都可以通过同一个基类来调用。

简单的说,某一个事物在不同状态下的多种状态

2、实现多态的三大前提

  • 必须要有继承关系
  • 要有方法的重写(思考可否不重写?)

        不是必须要重写的,重写可以体现子类的专属特征。

  • 要有父类的引用指向子类对象

使用多态第三个前提的注意事项:

        1.必须是继承关系的类,才可以写成父类的引用指向子类对象

        2.左边的父类,可以不必是子类对象的直接父类,也可以是父类的父类

代码体现

package day09;
class Demo1{

}

class Power{

}

class Water extends Power{
    public void getPower(){
        System.out.println("提供能量");
    }
}

class GutaiWater extends Water{
    @Override
    public void getPower(){
        System.out.println("可以嚼着吃");
    }
}



public class DuoTaiDemo1 {
    public static void main(String[] args) {
        //Demo1 w1 = new GutaiWater();   
        // 错误的,Demo1类与GutaiWater类没有继承关系
        Water w2 = new GutaiWater();//从右往左读,固态的水是水
        Power p1 = new GutaiWater();
    }
}

3、访问成员的特点

多态下,访问成员的特点:

1、成员变量

编译看左,允许看左。

2、成员方法

编译看左,运行看右。

思考:如何在多态的状态下,使用子类中特有的方法呢?

3、静态的成员方法

编译看左,运行看左。

package day09;

class Fu1{
    int a1 = 10;

    public void fun1(){
        System.out.println("这是父类中的非静态方法fun1");
    }
    public static void fun2(){
        System.out.println("这是父类中的静态方法fun2");
    }
}
 class Zi1 extends Fu1{
    int a = 11;
    @Override
     public void fun1(){
        System.out.println("子类中重写的非静态方法fun1");
    }

    public void show1(){
        System.out.println("天天向上,好好学习!");
    }

    public static void fun2(){
        System.out.println("这是子类中的静态方法fun2");
    }
 }

public class DuoTaiDemo2 {
    public static void main(String[] args) {
        //使用多态的方式创建一个子类对象
        Fu1 f1 = new Zi1();
        System.out.println(f1.a1); // 编译的时候,看左边父类中是否有该变量存在,若存在,编译不报错,运行的结果也是取父类中该变量的值。
        f1.fun1();// 编译的时候,看左边父类中是否有该方法存在,若存在,编译不报错,运行的结果也是取子类的中该方法的实现。
//        f1.show1();
//        f1.fun2(); // 静态的成员方法是跟随类走的,什么类型的变量调用静态方法,就是该类中的静态方法
        Fu1.fun2();
//        Zi1 zi1 = new Zi1();
//        System.out.println(zi1.a1);
    }
}

4、多态的好处

1.提高了程序的维护性(继承)

2.提高了程序的扩展性(多态)

可以从通用的基类继承出新的数据类型,从而新添加一些功能,而那些操纵基类接口的方法不需要任何改动就可以应用于新类。

package day09;

import java.nio.channels.Pipe;

/*
    多态的好处
        1、提高了程序的维护性(由继承保证)
        2、提高了程序的扩展性(由多态保证)

 */
class Animal{
    String name;
    int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(){
        System.out.println("吃");
    }

    public void sleep(){
        System.out.println("睡");
    }
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("🐕吃🥩");
    }

    @Override
    public void sleep() {
        System.out.println("🐕侧着睡");
    }
}

class Pig extends Animal{
    @Override
    public void eat() {
        System.out.println("🐖吃杂食");
    }

    @Override
    public void sleep() {
        System.out.println("🐖趴着睡");
    }
}

class Snake extends Animal{
    @Override
    public void eat() {
        System.out.println("🐍吃🐀");
    }

    @Override
    public void sleep() {
        System.out.println("🐍盘着睡");
    }
}

class Tiger extends Animal{
    @Override
    public void eat() {
        System.out.println("🐅吃🥩");
    }

    @Override
    public void sleep() {
        System.out.println("🐅趴着睡");
    }
}

//写一个工具类,对每一个动物调用吃和睡的功能
class AnimalTool{
    private AnimalTool(){}

//    public static void useDog(Dog dog){
//        dog.eat();
//        dog.sleep();
//    }
//
//    public static void usePig(Pig pig){
//        pig.eat();
//        pig.sleep();
//    }
//
//    public static void useSnake(Snake snake){
//        snake.eat();
//        snake.sleep();
//    }

    public static void useAnimal(Animal animal){ //Animal animal = new Tiger();
        animal.eat();
        animal.sleep();
    }
}




public class DuoTaiDemo5 {
    public static void main(String[] args) {
        //我想一只🐕
        Dog d1 = new Dog();
//        d1.eat();
//        d1.sleep();
        //使用工具类的方式调用动物的吃和睡的功能
//        AnimalTool.useDog(d1);
        AnimalTool.useAnimal(d1);

        //我想一只🐖
        Pig p2 = new Pig();
//        p2.eat();
//        p2.sleep();
//        AnimalTool.usePig(p2);
        AnimalTool.useAnimal(p2);

        //我想养🐍
        //按照上面的写法,应该先写一个🐍的类,然后在类中重写eat和sleep方法
        //在工具类中添加一个调用🐍的吃和睡的方法
        Snake s1 = new Snake();
//        AnimalTool.useSnake(s1);
        AnimalTool.useAnimal(s1);

        //随着我们要养动物的种类越来越多,每一种动物都需要经过上面两个步骤
        //创建该动物的类我们能理解,这是必须要有的
        //但是第二步在工具类进行添加修改其实是有问题。
        //今后的开发中,工具类是不允许频繁修改的。
        //但是我们不添加方法的话,就没法使用工具类调用具体动物的功能
        //于是,我们就在想,怎么可以只在工具类中写一个方法,可以调用所有动物的功能呢?
        //可以使用多态来实现。
        //我现在养一只🐅
        Tiger t1 = new Tiger();
        AnimalTool.useAnimal(t1); // new Tiger()


    }
}

5、多态的弊端

弊端:在多态的形式下,无法使用子类中特有的成员方法

解决方案:向下转型(曹操和曹植的故事)

曹操和曹植的故事,曹操是曹植的父亲,曹植是曹操的儿子
class 曹操{
    public void skill(){
        带兵打仗
    }
}

class 曹植 extends 曹操{
    @Override
    public void skill(){
         下棋
    }

    public void zuoShi(){
        作诗
    }
}

某一天,曹操带兵打仗出城了,城里只有曹植,这时候刘备待人过来攻打城池,但是小兵只听曹操的指令。
为了守护城池,曹植想到一个办法,装爹,粘上胡子,穿上爹的衣服,调用跟爹一样的skill方法。
//向上转型
曹操 c1 = new 曹植();
c1.skill();
// c1.zuoShi(); 不能调用
曹操打仗回来了,曹植看到父亲回来后,不用继续装爹,做回自己,脱掉爹的衣服,撕掉假胡子
//向下转型
曹植 c2 = (曹植) c1;
c2.skill();
c2.zuoShi();

 代码展示:


package day09;
class Fu2{
    public void fun1(){
        System.out.println("Hello World");
    }
}

class Zi2 extends Fu2{
    @Override
    public void fun1(){
        System.out.println("Hello Java");
    }

    public void show1(){
        System.out.println("编程有意思");
    }
}

public class DuoTaiDemo3 {
    public static void main(String[] args) {
        Fu2 f1 = new Zi2();
        f1.fun1();
//            f1.show();//根据多态访问成员的特点,编译看左,父类中没有show1方法,编译报错。
        //向下转型
        Zi2 z1 = new Zi2();
        z1.show1();
    }
}

但是向下转型同样也会遇到问题,在Java中所有的转型都会得到检查,以便保证它是我们想得到的类型。如果不是,就会返回一个ClassCastException(类转型异常)。这种在运行期间对类型进行检查的行为被称为“运行时类型识别”(RTTI)。

6、抽象类

1、抽象方法

Java中提供一个叫做抽象方法的机制,这种方法是不完整的;仅有声明而没有方法体。

2、格式

        针对抽象的事物,java也提供了一个关键字,可以将抽象的概念,定义成一个抽象的类,这个类将来不能进行new的。
    这个关键字:abstract 

abstract void f();

3、抽象类的概述

包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。(否则,编译器就会报错。)

4、抽象类使用注意事项

  • 抽象类不能进行实例化
  • 抽象类中既可以存在具体的方法,也可以存在抽象方法,如果一个类中有抽象方法,这个类必须是抽象类
  • 抽象类可以被具体的类继承,但是如果是被具体的类继承的话,必须要重写抽象类中的所有抽象方法
  • 抽象类A继承抽象类B的时候,可以选择是否重写抽象方法,也可以都不重写,原因是上面的第2点
  • 抽象类无法进行实例化,但是却可以拥有构造方法,意义是为了将来父类做初始化,子类构造方法第一句话默认是super()
  • 抽象类中,可以存在变量,常量,静态的变量。
package day09
abstract class Animal{
    int a = 10;
    final int b = 20;
    static int c = 30;

    public Animal() {
    }

    public void eat1(){
        System.out.println("吃");
    }
    //抽象方法
    public abstract void eat2();

    public abstract void fun1();
}

abstract class Animal1 extends Animal{
    @Override
    public void eat2(){

    }
}

class Dog3 extends Animal{
    @Override
    public void eat2(){
        System.out.println("吃肉");
    }

    @Override
    public void fun1() {
        System.out.println("好好学习,天天向上!");
    }
}

public class AbstractDemo1 {
    public static void main(String[] args) {
       // Animal animal = new Animal();//创建对象为抽象类型,编译器会认为不安全,会得到一条出错信息。
        Dog3 d1 = new Dog3();
        d1.eat2();
        d1.fun1();

        Animal a1 = new Dog3();
        a1.eat1();
        a1.eat2();
        a1.fun1();
        System.out.println(d1.a);
        System.out.println(d1.b);
        System.out.println(Animal.c);
    }
}

 5、abstract不能和哪些关键字共存

  •         private    冲突
  •         final    冲突
  •         static    无意义

    以后我们常见的修饰符组合:
        1、public static
        2、public abstract

package day09

abstract class Demo2{
//    private abstract void fun1();//非法的修饰符组合:abstract和private
//    private修饰的方法不能被子类继承,但是abstract方法将来是要被重写的,也冲突了。
//    public abstract final void fun1();//非法的修饰符组合:abstrac
//    public static abstract void fun1();//非法的修饰符组合:abstract和static
}

public class AbstractDemo3 {
    public static void main(String[] args) {
        
    }
}

7、接口

1、接口的概念

        一个接口表示:“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用给接口的哪些方法,而且仅需知道这些。因此,接口被用来建立类与类之间的协议(某些面向对象编程语言使用关键字protocol来完成这一功能。)

2、关键字interface

interface这个关键字会产生一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名、参数列和返类型,但是没有任何方法体。接口只提供了形式,而未提供任何具体实现。

格式:

 interface 接口名{

    }

使用接口的注意事项:

  • 接口中只能出现抽象方法,不能写具体的方法
  • 一个具体的类实现接口的话,需要使用关键字:implements
  • 若一个具体的类实现接口的时候,必须要实现接口中所有的抽象方法
  • 一个具体的类可以同时实现多个接口,但是必须要所有的抽象方法
  • 抽象也可以实现一个或多个接口
package day09

//定义一个骑车的接口
interface Cycling{
//    public void qiChe(){
//        System.out.println("骑车");
//    }

    public abstract void qiChe();
}

interface JiSuan{
    public abstract void calculation();
}

abstract class Animal4 implements Cycling,JiSuan{
    // public abstract void qiChe();
    // public abstract void calculation();
}

//普通的狗狗
class Dog4{
}

//会骑车的狗狗
class QiCheDog4 implements Cycling, JiSuan{
    @Override
    public void qiChe() {
        System.out.println("狗狗骑车");
    }

    @Override
    public void calculation() {
        System.out.println("狗狗会做高数");
    }
}

public class InterfaceDemo1 {
    public static void main(String[] args) {
        QiCheDog4 q = new QiCheDog4();
        q.calculation();
        q.qiChe();
    }
}

 3、接口和接口之间是继承关系

 一个可以继承若干个接口,可以多继承。

    面试题:java中允许多继承吗?
        1、类与类之间只允许单继承,不允许多继承,但是可以多层继承,形成继承体系
        2、接口与接口允许多继承

package day09;

interface C1{
    public abstract void fun5();
    public abstract void fun6();
}

interface B1{
    public abstract void fun2();
    public abstract void fun3();
    public abstract void fun4();
}

interface A1 extends B1,C1{
    //    public abstract void fun2();
    //    public abstract void fun3();
    //    public abstract void fun4();
    public abstract void fun1();
}

class Demo3 implements A1{

    @Override
    public void fun1() {

    }

    @Override
    public void fun2() {

    }

    @Override
    public void fun3() {

    }

    @Override
    public void fun4() {

    }

    @Override
    public void fun5() {

    }

    @Override
    public void fun6() {

    }
}



public class InterfaceDemo2 {
    public static void main(String[] args) {

    }
}

 4、接口中成员变量的特点

成员变量:

        接口中的变量默认都是被添加修饰符:public static final

        换句话说,接口中只允许出现常量

构造方法:

        接口中没有构造方法,自己写都不行,接口不能实例化

成员方法:

        接口只能是抽象方法,默认会在方法前加上修饰符:public abstract

package day09;

interface Inter1{
//    Inter1(){
//
//    }
    public static final int a = 10;
    public abstract void fun1();
}

class Inter1Impl implements Inter1{
    public void fun1(){
//        a=100;//java无法为最终变量a分配值
        System.out.println(a);
    }
}


public class InterfaceDemo2 {
    public static void main(String[] args) {
        Inter1Impl inter1 = new Inter1Impl();
        inter1.fun1();;
        System.out.println(Inter1.a);

//        Inter1 inter2 = new Inter1();
    }
}


interface StudentDao{
    public abstract void insert();
    public abstract void delete();
    public abstract void update();
    public abstract void select();
}

class StudentDaoImpl implements StudentDao {
    @Override
    public void insert(){

    }

    @Override
    public void delete(){

    }

    @Override
    public void update() {

    }

    @Override
    public void select() {

    }
}

 5、特点总结

  • 接口中只有常量,所有的变量将来默认会使用public static final进行修饰
  • 接口中只允许出现抽象方法,所有的方法将来默认会使用public abstract进行修饰
  • 具体类实现接口使用implements关键字,当具体的类实现接口的时候,必须要实现接口中所有的抽象方法
  • 一个类可以同时实现多个不同接口
  • 接口与接口之间的关系是继承关系,一个接口可以同时继承多个接口
  • 接口无法实例化

8、形式参数和返回值类型对比

1、形式参数

基本类型:简单,不提

引用类型:

(1) 类:当你看到一个类作为方法参数类型的时候,将来调用时需要传递该类及其该类的子类对象

package day10
class Demo1{
    public void fun1(){
        System.out.println("好好学习,天天向上!");
    }
}

class Demo1Zi extends Demo1{

}

class Demo1Test{
    public void show(Demo1 demo1){//Demo1 demo1 = new Demo1Zi()
        demo1.fun1();
    }
}

public class StudentDemo1 {
    public static void main(String[] args) {
        Demo1Test demo1Test = new Demo1Test();
        demo1Test.show(new Demo1());
        demo1Test.show(new Demo1Zi());
    }
}

(2) 抽象类:当你看到一个抽象类作为方法的参数类型的时候,将来调用时需要传递继承该抽象类的具体子类对象

package day10;

abstract class Demo2{
    public abstract void fun1();
}

class Demo2Zi extends Demo2{
    @Override
    public void fun1(){
        System.out.println("好好学习,天天向上!");
    }
}

class Student2{
    public void show(Demo2 demo2){//Demo2 demo2 = new Demo2Zi()
        demo2.fun1();
    }
}

public class StudentDemo2 {
    public static void main(String[] args) {
        Student2 student2 = new Student2();
        student2.show(new Demo2Zi());
    }
}

(3)接口:当你看到一个接口作为方法参数类型的时候,将来调用时需要传递实现该接口的具体子类对象 

package day10;
//接口:当你看到一个接口作为方法参数类型的时候,将来调用时需要传递实现该接口的具体子类对象

interface Inter1{
    public abstract void fun1();
}

class Inter1Impl implements Inter1{
    @Override
    public void fun1(){
        System.out.println("好好学习,天天向上!");
    }
}

class Student3{
    public void show(Inter1 inter1){//Inter1 inter1 = new Inter1Ipml()接口多态
        inter1.fun1();
    }
}

public class StudentDemo3 {
    public static void main(String[] args) {
        Student3 student3 = new Student3();
        student3.show(new Inter1Impl());
    }
}

 2、返回值类型对比

(1)类:当你看到一个类作为方法的返回值类型的时候,将来方法内部应该返回该类或该类的子类对象

package day10;

//类:当你看到一个类作为方法的返回值类型的时候,将来方法内部应该返回该类或该类的子类对象
class Student1{
    public void fun1(){
        System.out.println("好好学习,天天向上!");
    }
}

class Demo1{
    public Student1 show(){
        return new Student1();
    }
}

public class StudentDemo1 {
    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        Student1 s1 = demo1.show(); // Student1 s1 = new Student1()
        s1.fun1();
        System.out.println("s1: "+s1);

        Student1 s2 = demo1.show();
        System.out.println("s2: "+s2);

    }
}

(2)抽象类:当你看到一个抽象类作为方法的返回值类型的时候,将来方法内部应该返回继承该抽象类的具体子类对象

package day10;

//抽象类:当你看到一个抽象类作为方法的返回值类型的时候,将来方法内部应该返回继承该抽象类子类对象
            
abstract class Demo2{
    public abstract void fun1();
}

class Demo2Zi extends Demo2{
    @Override
    public void fun1() {
        System.out.println("张三是阿里巴巴的高级开发程序员");
    }
}

class Student2{
    public Demo2 show(){
        return new Demo2Zi();
    }
}

public class StudentDemo2 {
    public static void main(String[] args) {
        Student2 student2 = new Student2();
        Demo2 d1 = student2.show(); //Demo2 d1 = new Demo2Zi() 抽象多态
        d1.fun1();
    }
}

(3)接口:当你看到一个接口作为方法的返回值类型的时候,将来方法内部应该返回实现了该接口的具体子类对象

package day10;
//接口:当你看到一个接口作为方法的返回值类型的时候,将来方法内部应该返回实现了该接口的具体子类对象
interface Inter1{
    public abstract void fun1();
}

class Inter1Impl implements Inter1{
    @Override
    public void fun1() {
        System.out.println("昨天南京下冰雹了");
    }
}

class Student3{
    public Inter1 show(){
        return new Inter1Impl();
    }
}

public class StudentDemo3 {
    public static void main(String[] args) {
        Student3 student3 = new Student3();
        Inter1 i1 = student3.show(); //Inter1 i1 = new Inter1Impl() 接口多态
        i1.fun1();

//        Student3 student4 = new Student3();
//        Inter1 i2 = new Student3().show();
         new Student3()
                .show()
                .fun1(); // 调用方式:链式调用
    }
}

9、包

1、包的划分

  • 根据功能进行划分
  • 根据角色进行划分
包:其实就是文件夹(目录)
创建包的好处:
    1、帮助我们管理代码文件
    2、不同的包下名字重复

以后创建包的话常用的划分方式:
    1、按照功能划分:
        增加功能
            学生增加
            老师增加
        删除功能
            学生删除
            老师删除
        修改功能
            学生修改
            老师修改
        查询功能
            学生查询
            老师查询

    2、按照角色划分
        学生
          学生增加
          学生删除
          学生修改
          学生查询
        老师
          老师增加
          老师删除
          老师修改
          老师查询

若每一个维度会经常改变,就不要将其设计成包

//课外了解
spring boot开发中常用建包方式:
    controller 控制类层(主要是与前端页面做交互的)
    entity(pojo)实体类层
    dao 数据库操作层
    service 服务接口层
        - serviceImpl 服务实现类层

2、导包

import 包名;

在package和所有的class之间写。

10、权限修饰符

常用的修饰符:
        权限修饰符:public,protected,默认的,private
        静态修饰符:static
        常量修饰符:final
        抽象修饰符:abstract


    类:
        权限修饰符:public,默认的
        常量修饰符:final
        抽象修饰符:abstract
    成员变量:
        权限修饰符:public,protected,默认的,private
        静态修饰符:static
        常量修饰符:final
    构造方法:
        权限修饰符:public,protected,默认的,private
    成员方法:
        权限修饰符:public,protected,默认的,private
        静态修饰符:static
        常量修饰符:final
        抽象修饰符:abstract

   public
   public abstract
   public static
   public static final

package day10;

public class Student {
    public String name;
    protected int age;
    String address;
    private String phoneNum;


    public void fun1(){
        System.out.println(name);
        System.out.println(age);
        System.out.println(address);
        System.out.println(phoneNum);
    }
}
package day10;

public class StudentZi extends Student{
    public void show1(){
        System.out.println(name);
        System.out.println(age);
        System.out.println(address);
//        System.out.println(phoneNum);
    }
}
package day10;

import day10.Student;

public class StudentZi2 extends Student {
    public void show2(){
        System.out.println(name);
        System.out.println(age);
//        System.out.println(address);
//        System.out.println(phoneNum);
    }
}
package day10;

import day10.Student;

public class StudentDemo {
    public void fun4(){
        Student student = new Student();
        System.out.println(student.name);
//        System.out.println(student.age);
//        System.out.println(student.address);
//        System.out.println(student.phoneNum);
    }

11、内部类

1、概述及分类

内部类:将一个类A定义在一个类B中,这个类A称之为内部类
    分类:
        成员内部类:将类定义在一个类中的成员位置上
        局部内部类:将类定义在一个方法中

package day10;


class Outer1{
    int a1 = 10;
    private int a2 = 11;
    public static int a3 = 12;

    class Inner1{
        public void fun1(){
            a1 = 20;
            System.out.println(a1);
            a2 = 30;
            System.out.println(a2);
            a3 = 40;
            System.out.println(a3);
        }
    }
}

public class InnerDemo1 {
    public static void main(String[] args) {
        //外部类类名.内部类类名 对象名 = new 外部类类名().new 内部类类名();
        Outer1.Inner1 inner1 = new Outer1().new Inner1();
        inner1.fun1();
    }
}

 2、格式

外部类类名.内部类类名 对象名 = new 外部类类名().new 内部类类名()。

3、内部类的修饰符

(1)static

(2)private

package day10;

/*
    内部类常用的修饰符:
        static
        private
 */
//class Outer2{
//    static int a1 = 10;
//    private static int a2 = 11;
//    public static int a3 = 12;
//
//    static class Inner1{
//        public void fun1(){
//            System.out.println(a1);
//            System.out.println(a2);
//            System.out.println(a3);
//        }
//    }
//}

class Outer3 {
    private class Inner {
        public void fun1(){
            System.out.println("好好学习,天天向上!");
        }
    }

    public void show(){
        Inner inner = new Inner();
        inner.fun1();
    }
}

public class InnerDemo2 {
    public static void main(String[] args) {
//        Outer2.Inner1 inner1 = new Outer2.Inner1();
//        inner1.fun1();

        Outer3 outer3 = new Outer3();
        outer3.show();
    }
}
package day10;

class Outer {
    public int num = 10;
    class Inner {
        public int num = 20;
        public void show() {
            int num = 30;
            System.out.println(num); // 30
            System.out.println(this.num); // 20
            System.out.println(Outer.this.num); // 10
        }
    }
}

public class InnerDemo3 {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer().new Inner();
        inner.show();

    }

}
package day10;

class Outer4 {

    //成员方法
    public void fun1() {
        //局部变量
        int a = 10; // 如果局部内部类中要使用方法中的局部变量的话,这个变量会被使用final关键字进行修饰
        //局部内部类
        class Inner {
            int b = 20;
            public void show() {
//                a = 11; //  从内部类引用的本地变量必须是最终变量或实际上的最终变量
                b = 12;
                System.out.println(a);
                System.out.println(b);
            }
        }
        Inner inner = new Inner();
        inner.show();
    }
}

public class InnerDemo4 {
    public static void main(String[] args) {
        Outer4 outer4 = new Outer4();
        outer4.fun1();

    }
}

12、匿名内部类

1、格式

匿名内部类:
        语法定义格式:
            new 抽象类/接口(){
               //要重写的方法
            }

package com.shujia.day10.bao8;


/*
    匿名内部类:
        语法定义格式:
            new 抽象类/接口(){
               //要重写的方法
            }
 */
abstract class Demo1{
    public abstract void fun1();

//    public abstract void fun2();
}

//class XXX extends Demo1{
//    @Override
//    public void fun1() {
//        System.out.println("hello world");
//    }
//}
//
//class Demo1Zi2 extends Demo1{
//    @Override
//    public void fun1() {
//        System.out.println("好好学习,天天向上!");
//    }
//}

class Student1{
    public void show1(Demo1 demo1){
        demo1.fun1();
    }
}

public class NiMingClassDemo1 {
    public static void main(String[] args) {
        Student1 student1 = new Student1();
//        student1.show1(new Demo1Zi());
//        student1.show1(new Demo1Zi2());
        /**
         *  相当于jvm给我们做了几件事情
         *  1、jvm底层自己造了一个类继承抽象类或者实现接口,这个类没有名字
         *  2、重写的方法(实现的方法),我们只需要关注重写的方法
         *  3、将这个类的对象造出来
         */
        student1.show1(new Demo1() {
            @Override
            public void fun1() {
                System.out.println("hello world");
            }
        });

        student1.show1(new Demo1() {
            @Override
            public void fun1() {
                System.out.println("好好学习,天天向上");
            }
        });

    }
}

 2、接口类型的方法调用,使用匿名内部类

package day10;

/*
    接口类型的方法调用,使用匿名内部类
    匿名内部类:
        语法定义格式:
            new 抽象类/接口(){
               //要重写的方法
            }
 */

interface Inter1{
    void fun1();
}

//class Inter1Impl implements Inter1{
//    @Override
//    public void fun1() {
//        System.out.println("hello java");
//    }
//}


class Student2{
    public void show(Inter1 inter1){
        inter1.fun1();
    }
}

public class NiMingClassDemo2 {
    public static void main(String[] args) {
        Student2 student2 = new Student2();
//        student2.show(new Inter1Impl());
        student2.show(new Inter1(){
            @Override
            public void fun1(){
                System.out.println("hello 匿名类");
            }
        });

        student2.show(new Inter1(){
            @Override
            public void fun1(){
                System.out.println("hello java");
            }
        });

//        student2.show(()-> System.out.println("hello world")); // lambda表达式
    }
}

13、向上转型和向下转型

1、向上转型

好处:隐藏了子类型,提高了代码的扩展性。
坏处:只能使用父类的功能,不能使用子类特有功能,功能被限定。
使用场景:不需要面对子类型,通过提高扩展性,或者使用父类的功能即可完成操作,就是使用向上转型。

2、向下转型

好处:可以使用子类型的特有功能
坏处:面对具体的子类型,向下转型具有风险。即容易发生ClassCastException,只要转换类型和对象不匹配就会发生。解决方法:使用关键字instanceof。

14、浅拷贝和深拷贝

1、二者的区别

浅拷贝:在拷贝一个对象时,对对象的基本数据类型的成员变量进行拷贝,但对引用类型的成员变量只进行引用的传递,并没有创建一个新的对象,当对引用类型的内容修改会影响被拷贝的对象。

深拷贝:在拷贝一个对象时,除了对基本数据类型的成员变量进行拷贝,对引用类型的成员变量进行拷贝时,创建一个新的对象来保存引用类型的成员变量。

2、浅拷贝和深拷贝的应用

浅拷贝

java中clone方法是一个浅拷贝,引用类型依然在传递引用

深拷贝

实现深拷贝有两种方法:

(1) 序列化该对象,然后反序列化回来,就能得到一个新的对象了。

序列化:将对象写入到IO流中; 反序列化:从IO流中恢复对象 序列化机制允许将实现序列化的java对象转化为字节序列,这些字节序列可以保存到磁盘或者网络传输上,以达到以后恢复成原来的对象,序列化机制使得对象可以脱离程序的运行而独立存在。

(2) 继续利用clone()方法,对该对象的引用类型变量再实现一次clone()方法。

15、练习

-- 1、假如我们在开发一个系统时需要对员工类进行设计,员工包含3个属性:姓名、工号以及工资。
经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。
请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
-- 2、
编写程序实现比萨制作。需求说明编写程序,接收用户输入的信息,选择需要制作 的比萨。可供选择的比萨有:培根比萨和海鲜比萨。
实现思路及关键代码
1)     分析培根比萨和海鲜比萨
2)     定义比萨类
3)     属性:名称、价格、大小
4)     方法:展示
5)     定义培根比萨和海鲜比萨继承自比萨类
6)     定义测试类,根据输入信息产生具体的比萨对象 
程序运行结果如图所示

 

-- 3、
编写程序实现软料购买:编写程序,接收用户输入的信息,选择购买的饮料。可供 选择的饮料有:咖啡、矿泉水和可乐。其中,购买咖啡时可以选择:加糖、加奶还
是什么都不加。购买可乐时可以选择:买可口可乐还是百事可乐。 程序运行效果如图所示。

 

16、答案

-- 1、

/*
分析:
        父类:员工类(抽象类)
        子类1:普通员工类 extends 员工类
        子类2:经理类 extends 员工类
*/

package day09;

abstract class Staff{
    private String name;
    private String id;
    private int salary;

    public Staff() {
    }

    public Staff(String name, String id, int salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
    public abstract void work();
}

class Employee extends Staff{

    public Employee() {
    }

    public Employee(String name, String id, int salary) {
        //  this.name = name;
        //  this.id = id;
        //  this.salary = salary;
        super(name, id, salary);
    }

    @Override
    public void work() {
        System.out.println("牛马苦搬砖");
    }
}

class Manager extends Staff{
    private int bonus;

    public Manager() {
    }

    public Manager(String name, String id, int salary, int bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }

    public int getBonus() {
        return bonus;
    }

    public void setBonus(int bonus) {
        this.bonus = bonus;
    }

    @Override
    public void work() {
        System.out.println("老板笑嘻嘻");
    }
}


public class AbstractDemo2 {
    public static void main(String[] args) {
        Staff e1 = new Employee("张三", "gs1007", 1000); //抽象多态
        e1.work();
        //创建一个经理对象
        Staff m1 = new Manager("李四", "gs1001", 1000000, 1000000000);
        m1.work();
    }
}
   




-- 2、
package day10;
import com.sun.org.apache.xpath.internal.objects.XString;

import java.util.Scanner;

class  Pizza{
    private String name;
    private double price;
    private int size;

    public Pizza() {
    }

    public Pizza(String name, double price, int size) {
        this.name = name;
        this.price = price;
        this.size = size;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public void show(){

    };
}





class Bacon_Pizza extends Pizza{
    private int bacon;

    public Bacon_Pizza() {
    }

    public Bacon_Pizza(String name, double price, int size, int bacon) {
        super(name, price, size);
        this.bacon = bacon;
    }

    public int getBacon() {
        return bacon;
    }

    public void setBacon(int bacon) {
        this.bacon = bacon;
    }

    @Override
    public void show(){
        System.out.println("名称:"+getName());
        System.out.println("价格:"+getPrice()+"元");
        System.out.println("大小:"+getSize()+"寸");
        System.out.println("培根克数:"+getBacon());
    }

}

class Seafood_pizza extends Pizza{
    private String Seafood;

    public Seafood_pizza() {
    }

    public Seafood_pizza(String name, double price, int size, String seafood) {
        super(name, price, size);
        Seafood = seafood;
    }

    public String getSeafood() {
        return Seafood;
    }

    public void setSeafood(String seafood) {
        Seafood = seafood;
    }

    @Override
    public void show(){
        System.out.println("名称:"+getName());
        System.out.println("价格:"+getPrice()+"元");
        System.out.println("大小:"+getSize()+"寸");
        System.out.println("配料:"+getSeafood());
    }

}

public class HomeWork01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(1==1){
            System.out.println("请选择想要制作的比萨(1.培根披萨  2.海鲜披萨  3.退出):");
            int num = sc.nextInt();
            if(num==1){
                Bacon_Pizza b = new Bacon_Pizza();
                b.setName("培根披萨");
                System.out.println("请输入培根克数:");
                b.setBacon(sc.nextInt());
                System.out.print("请输入披萨大小:");
                b.setSize(sc.nextInt());
                System.out.print("请输入披萨价格:");
                b.setPrice(sc.nextDouble());
                b.show();
            } else if (num==2) {
                Seafood_pizza s = new Seafood_pizza();
                s.setName("海鲜披萨");
                System.out.println("请输入配料信息:");
                s.setSeafood(sc.next());
                System.out.print("请输入披萨大小:");
                s.setSize(sc.nextInt());
                System.out.print("请输入披萨价格:");
                s.setPrice(sc.nextDouble());
                s.show();
            }else if(num==3){
                break;
            }
        }
    }
}




-- 3、
package day10;

import java.util.Scanner;

class  Drink{
    private String name;
    private int volume;

    public Drink() {
    }

    public Drink(String name, int volume) {
        this.name = name;
        this.volume = volume;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getVolume() {
        return volume;
    }

    public void setVolume(int volume) {
        this.volume = volume;
    }

    public void show(){

    }
}

class Coffee extends Drink{
    @Override
    public void show(){
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入购买容量:");
        super.setVolume(sc.nextInt());
        System.out.print("请问是否要配料:(1、加糖 2、加奶 3、什么都不加):");
        int num = sc.nextInt();
        System.out.println("您购买饮料信息如下。");
        System.out.println("名称:咖啡");
        if(num == 1){
            System.out.println("添加配料:加糖");
        } else if (num == 2) {
            System.out.println("添加配料:加奶");
        }else{
            System.out.println("添加配料:无");
        }
        System.out.println("容量:"+getVolume());
    }
}

class Water extends Drink{
    @Override
    public void show() {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入购买容量:");
        super.setVolume(sc.nextInt());
        System.out.println("您购买饮料信息如下。");
        System.out.println("名称:矿泉水");
        System.out.println("容量:"+getVolume());
    }
}

class Cola extends Drink{
    @Override
    public void show() {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入购买容量:");
        super.setVolume(sc.nextInt());
        System.out.print("请问需要哪一款可乐:(1.可口可乐 2.百事可乐):");
        int num = sc.nextInt();
        if(num == 1){
            System.out.println("名称:可口可乐");
        } else if (num == 2) {
            System.out.println("名称:百事可乐");
        }
        System.out.println("容量:"+getVolume());
    }
}

public class HomeWork02 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("请选择饮料(1、咖啡 2、矿泉水 3、可乐):");
        int num = sc.nextInt();
        if(num == 1){
            Coffee c = new Coffee();
            c.show();
        } else if (num == 2) {
            Water w = new Water();
            w.show();
        } else if (num == 3) {
            Cola cl = new Cola();
            cl.show();
        }
    }
}

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值