Java面向对象下集

目录

1、复用类

1.1 类之间的关系

1.2 组合

1.3 继承

1.4 继承特点

1.5 extends关键字

1.6 super关键字

1.6.1 子类访问父类的成员变量

1.6.2 super调用父类构造方法

1.6.3 super调用父类的方法

1.7 重写(Override)

1.8 instanceof 关键字

1.9 final关键字

2、多态

2.1 抽象类

2.2 java数值交换

2.3 接口

2.4 多态

2.5 内部类  

2.5.1 成员内部类

2.5.2 嵌套类(静态内部类)

2.5.3 局部内部类

2.5.4 匿名内部类


1、复用类

        提高代码复用性是Java具备的功能之一。Java中有两种方式实现该目的的方法。第一种是在新的类中产生现有类的对象,新的类是有现有的对象所组成,这就是组合。例如:描述汽车类,汽车应该是有车轮子,车窗,发动机等对象组成。

        第二种方法是按照现有类的类型创建新类,这就是继承。例如,已知一个Person类,需要定义两个类,分别描述学生,工人。这些类有很多的共性(都是Person),可以通过继承来设计这些类,提高代码的复用性。

1.1 类之间的关系

组合与继承

1.2 组合

1:现实生活

笔记本包含 cpu 整体与部分的关系

   航母编队 包含(航母 护卫舰 驱逐舰 舰载机 潜艇)

Java中创建Person对象,人出生应该具备出生地的。所以在描述Person类时,需要一个Address类。

1:描述Person类,描述地址(Address)类,Person类需要维护Address

          在Person类中只需要维护一个Address类的引用即可。

class Person{

    String name;

    int age;

    Address add;

    Person(){

    }

    Person(String name,int age,Address add){

       this.name=name;

       this.age=age;

       this.add=add;

    }

   

    void speak(){

       System.out.println("姓名:"+name+" 年龄:"+age+" "+add.print());

    }

}

class Address{

    String country;

    String city;

    String street;

    Address(){

    }

    Address(String country,String city,String street){

       this.country=country;

       this.city=city;

       this.street=street;

    }

    String print(){

       return "地址:"+country+" "+"城市:"+city+街道;"+street;

    }

}

class Demo3

    public static void main(String[] args){

       Address add=new Address("中国","广州","棠东东路");

       Person p=new Person("jack",27,add);

       p.speak();

        System.out.println();

        }

}

1.3 继承

1:现实生活中一些事物之间是有一定的关系的。例如:学生是人,猫是猫科动物。

Java中的体现

   1:描述一个学生类

      1:姓名年龄属性,学习的方法

   2:描述一个工人类

      1:姓名年龄属性,工作的方法

   3:描述一个人类

      1:姓名年龄属性,说话的方法。

4:发现学生类和人类有关系,学生和工人都是人。所以人有的属性和行为学生和工人都会有。出现了类与类之间代码重复

class Person {

    String name;

    int age;

    // 静态变量(类变量)对象和对象之间的代码重复使用静态变量

    static String country = "CN";

    Person() {

    }

    void speak() {

       System.out.println(name + ":哈哈,我是人!!!");

    }

}

// 让学生类和人类产生关系,发现学生is a 人,就可以使用继承

class Student {

    String name;

    int age;

    Student() {

    }

    void study() {

       System.out.println("姓名:" + name + "年纪:" + age + ":好好学习");

    }

}

class Worker {

    String name;

    int age;

    void work() {

       System.out.println(name + ":好好工作,好好挣钱。");

    }

}

class Demo1 {

    public static void main(String[] args) {

       Student s = new Student();

       s.name = "jack";

       s.age = 20;

       s.study();

       Worker w = new Worker();

       w.name = "rose";

       w.work();

    }

}

总结:发现问题:

      1:出现类和类有关系

它们之间的关系无法描述。

具备共性的代码出现重复定义。

1.4 继承特点

   1:描述类和类之间的关系

   2:降低类和类之间的重复代码

1:降低对象和对象之间的代码重复使用静态变量

   2:降低类和类之间的代码重复使用就继承

但是一定要符合is a 的关系。

  

1.5 extends关键字

继承是所有的OOP语言和Java语言中不可缺少的组成部分。

Java中的继承使用extends关键字实现。

1:学生是人,工人是人。

2:谁继承谁?

   学生继承人,发现学生里的成员变量,姓名和年龄,人里边也都进行了定义。有重   复代码将学生类的重复代码注释掉,创建学生类对象,仍然可以获取到注释的成员。这就是因为继承的关系,学生类(子类)继承了人类(父类)的部分

class Person {

    String name;

    int age;

    // 静态变量(类变量)对象和对象之间的代码重复使用静态变量

    static String country = "CN";

    Person() {

    }

    void speak() {

       System.out.println(name + ":哈哈,我是人!!!");

    }

}

// 让学生类和人类产生关系,发现学生is a 人,就可以使用继承

class Student extends Person {

    Student() {

    }

    void study() {

       System.out.println("姓名:" + name + "年纪:" + age + ":好好学习");

    }

}

class Worker extends Person {

    void work() {

       System.out.println(name + ":好好工作,好好挣钱。");

    }

}

class Demo1 {

    public static void main(String[] args) {

       Student stu = new Student();

       stu.name = "jack";

       stu.age = 20;

       stu.study();

       stu.speak();

       System.out.println(stu.country);

       System.out.println(Student.country);

       Worker worker = new Worker();

       worker.name = "rose";

       System.out.println(worker.country);

       worker.work();

       worker.speak();

       System.out.println();

    }

}

案例:测试继承

子类并能否继承父类中所有的成员?

提示:

1:父类定义完整的成员 静态成员变量,非静态成员变量(私有和非私有),构造方法(有参数构造无参数构造)静态方法,非静态成员方法。

2:创建子类继承父类。

    尝试在子类中访问父类的private成员变量。

   尝试一个子类继承多个父类。

3:创建子类对象,尝试通过子类对象名来访问父类的成员。

  结论:

子类并不能继承父类中所有的成员

   1.父类的所有的私有成员不能继承(private修饰的成员)

   2.构造函数不能被继承

   3.Java只支持单继承,不支持多继承。

3:如何使用继承

继承的出现是为了描述is a关系出现的,不要为了仅仅使用某个类的方法,就盲目的继承一个类。一个父类和它的子类之间必须是存在关系的。

1:不要为了使用继承而继承。工人和学生都有共性的成员,不要为了节省代码,让工人继承学生。

/*

如何使用继承:验证是否有 is  a 的关系

 例如:学生是人, 小狗是动物

 注意:不要为了使用某些功能而继承,java只支持单继承

 */

class DK {

    void Ip4S() {

       System.out.println("好玩");

    }

}

class BGir extends DK {

}

class Demo {

    public static void main(String[] args) {

       new BGir().Ip4S();

    }

}

理解继承细节;  

   1:类名的设定,被继承的类称之为父类(基类),继承的类称之为子类

   2:和传统的理解相反,子类并不是父类的一个子集。子类应当比父类包含更多的属性和行为。

   3:父类的所有的私有成员不能继承

   4:使用继承要符合is a关系。不要为了使用继承而继承

   5:Java只支持单一继承,不支持多继承但是可以多重继承例如C继承B, B继承A

1.6 super关键字

子类继承它的父类中所有可以访问的成员变量和方法。子类不能够继承父类构造方法,但是父类的构造方法能从子类调用吗?学过了关键字this,都知道this关键字是一个当前对象引用通过this可以调用本类构造方法。如果当前对象有父类。如何在子类调用父类的构造方法?可以使用super关键字。那么super关键字的出现是指的是该类的父类。

super用途:

super关键字可以用于调用父类的构造方法。

super关键字可以调用父类的成员

1.6.1 子类访问父类的成员变量

案例:

   1:定义Father(父类)类

      1:成员变量int x=1;

      2:构造方法无参的和有参的,有输出语句

   2:定义Son类extends Father类

      1:成员变量int y=1;

      2:构造方法无参和有参的。有输出语句

      1:this.y=y+x;

   3:创建Son类对象

      Son son=new Son(3);

      System.out.println(son.y); //4

class Father {

    int x = 1;

    Father() {

       System.out.println("这是父类无参构造");

    }

    Father(int x) {

       this.x = x;

       System.out.println("这是父类有参构造");

    }

    void speak() {

       System.out.println("我是父亲");

    }

}

class Son extends Father {

    int y = 1;

    Son() {

       System.out.println("这是子类的无参构造");

    }

    Son(int y) {

       this.y = y + x;

       System.out.println("这是子类的有参构造");

    }

    void run() {

       super.speak(); // 访问父类的函数

       System.out.println("我是儿子");

    }

}

class Demo6 {

    public static void main(String[] args) {

       Son s = new Son(3);

       System.out.println(s.y);// 4

    }

}

    4:子类对象为什么可以访问父类的成员。

      1:this.y=y+x;有一个隐式的super super.x

   5:super关键字作用

      1:主要存在于子类方法中,用于指向子类对象中父类对象(不严谨)。

      2:访问父类的属性

      3:访问父类的函数

      4:访问父类的构造函数

   6:super注意

this和super很像,this指向的是当前对象的调用,super指向的是当前调用对象的父类。Demo类被加载,执行main方法,Son.class加载,发现有父类Father类,于是Father类也加载进内存。类加载完毕,创建对象,父类的构造方法会被调用(默认自动无参),然后执行子类相应构造创建了一个子类对象,该子类对象还包含了一个父类对象。该父类对象在子类对象内部。this super只能在有对象的前提下使用,不能在静态上下文使用。

1.6.2 super调用父类构造方法

1.创建一个子类对象时,子类构造方法会在开始执行时,先去调用它的父类的构造方法。

案例:验证该说法

     提示:自定义父类(无参数构造),自定义子类(无参数构造),子类继承父类。

          使用子类无参数构造创建对象。测试父类无参数构造是否执行。

2.子类默认调用父类无参数构造

如果在子类的构造函数中没有显示的调用父类的构造方法,编译器会自动的在子类构造函数第一行使用super调用父类的无参数构造。

案例:验证该说法。

     提示:自定义父类(父类自定义有参数构造),自定义子类,子类继承父类。

总结:子类的构造函数默认第一行会默认调用父类无参的构造函数,隐式语句super();

     如果父类无参构造函数不存在,编译报错。

3.子类显式调用父类构造函数

在子类构造函数第一行通过super关键字调用父类任何构造函数。如果显式调用父类构造函数,编译器自动添加的调用父类无参数的构造就消失。构造函数间的调用只能放在第一行,只能调用一次。super() 和this()不能同时存在构造函数第一行。

案例:验证上述说法

1.6.3 super调用父类的方法

   关键字不仅仅可以引用父类的构造方法,也可以引用父类的方法。格式如下;

super.父类方法名();

class Father {

    void run(){

       System.out.println("father");

    }

}

class Son extends Father {

    public Son() {

    }

    void sleep(){

       super.run();

    }

}

本案例中super是没有必要的,因为子类继承了父类。那么什么时候才需要使用super呢?在即将学到的重写中就可以看到。

4:super思考

如果开发者自定义了一个类,没有显示的进行类的继承,那么该类中成员函数是否可以使用super关健健字?可以使用,继承了Object类,Object类是所有类的父类。

class Demo7 {

    public  void print(){

       System.out.println(super.toString());

    }

    public static void main(String[] args){

       new Demo7().print();

       System.out.println();

        }

}

1.7 重写(Override)

当子类继承父类,也就继承了父类的方法,有时候,子类需要修改父类中的方法。Java支持这种做法。叫做覆盖(overriding)

案例:测试覆盖

          提示:父类和子类中定义同名的函数。

1:定义Father类

   1:姓名,吃饭方法,吃窝窝头。

   2:定义Son类,继承Father

      1:Son类中不定义任何成员,子类创建对象,仍然可以调用吃饭的方法。

      2:父类的吃饭的方法,Son不愿吃。Son自己定义了吃饭的方法。

1:此时父类中有一个吃饭的方法,子类中有2个吃饭的方法,一模一样,只是方法体不一样。

          2:一个类中两个函数一模一样,是不允许的。

             1:编译运行,执行了子类的方法。

             2:使用父类的方法,在子类方法中,使用super.父类方法名。

class Father {

    String name;

    void eat() {

       System.out.println("吃窝窝");

    }

}

class Son extends Father {

    public void eat() { // 继承可以使得子类增强父类的方法

       System.out.println("来俩小菜");

       System.out.println("来两杯");

       System.out.println("吃香喝辣");

           System.out.println("来一根");

    }

}

class Demo8 {

    public static void main(String[] args) {

       Son s = new Son();

        //执行子类的方法

       s.eat();

    }

}

3:该现象就叫做重写(覆盖 override)

1: 在继承中,子类可以定义和父类相同的名称且参数列表一致的函数,将这种函数

称之为函数的重写.

4:前提

   1:必须要有继承关系

5:特点

1:当子类重写了父类的函数,那么子类的对象如果调用该函数,一定调用的是重写过后的函数。

      可以通过super关键字进行父类的重写函数的调用。

   2: 继承可以使得子类增强父类的方法

6:细节

   1: 函数名必须相同

   2:参数列表必须相同

3: 子类重写父类的函数的时候,函数的访问权限必须大于等于父类的函数的访

问权限否则编译报错

4:子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类。不能返回比父类更大的数据类型: 如子类函数返回值类型是Object

      1:定义 A B  C 类 B extends A 

      2:Father类中定义A getA();

      3:Son 类中重写getA(); 方法,尝试将返回值修改为B,C ,Object

          1:B编译通过

          2:C 编译失败 ,没有继承关系

          3:Object编译失败,比父类的返回值类型更大

class A {

}

class B extends A {

}

class C {

}

class Father {

    String name;

    void eat() {

       System.out.println("吃窝窝");

    }

    // 定义一个函数,获取A类的对象,

    A getA() {

       return new A();

    }

}

class Son extends Father {

    public void eat() { // 继承可以使得子类增强父类的方法

       System.out.println("来两杯");

       System.out.println("来俩小菜");

       super.eat();

       System.out.println("来一根");

    }

    // B类是A类的子类

    B getA() {

       return new B();

    }

}

class Demo8 {

    public static void main(String[] args) {

       Son s = new Son();

       s.eat();

    }

}

7:子类对象查找属性或方法时的顺序:

   1:原则:就近原则。

如果子类的对象调用方法,默认先使用this进行查找,如果当前对象没有找到属性或方法,找当前对象中维护的super关键字指向的对象,如果还没有找到编译报错,找到直接调用。

8:重载和重写的不同

   1:重载(overload): 

      1:前提: 所有的重载函数必须在同一个类中

        2:特点:

             函数名相同,参数列表不同,与其他的无关(访问控制符、返回值类型)

      3:不同:

          个数不同 、 顺序不同、 类型不同

   2:重写(override):

        1:前提: 继承

      2:特点:

            函数名必须相同、参数列表必须相同。

            子类的返回值类型要等于或者小于父类的返回值

9:重写练习

      描述不同的动物不同的叫法

      1:定义动物类

          有名字,有吃和叫的方法

      2:定义狗继承动物重写父类吃和叫的方法

      3:定义猫继承动物重写父类吃和叫的方法

class Animal{

    int x=1;

    String name;

    void eat(){

       System.out.println("吃东西");

    }

    void shout(){

       System.out.println("我是动物");

    }

}

class Dog extends Animal{

    void eat(){

       System.out.println("啃骨头");

    }

    void shout(){

       System.out.println("旺旺");

    }

    void eat(String food){

       System.out.println("吃:"+food);

    }

}

class Cat extends Animal{

    void eat(){

       System.out.println("吃老鼠");

    }

    void shout(){

       System.out.println("喵喵");

    }

}

class Demo9{

    public static void main(String[] args){

       Dog d=new Dog();

       d.shout();

       d.eat();

      

       Cat c=new Cat();

       c.shout();

       c.eat();

       System.out.println();

        }

}

1.8 instanceof 关键字

1:快速演示instanceof

Person p=new Person();

       System.out.println( p instanceof Person);

2:instanceof是什么?

   1:属于比较运算符:

   2:instanceof关键字:该关键字用来判断一个对象是否是指定类的对象。

   3:用法:

     对象  instanceof 类;  

    该表达式是一个比较运算符,返回的结果是boolea类型  true|false

3:案例

定义一个功能表函数,根据传递进来的对象的做不同的事情,如果是狗让其看家,如果是猫让其抓老鼠

      1:定义动物类

      2:定义狗类继承动物类

      3:定义猫类继承动物类

      4:定义功能根据传入的动物,执行具体的功能

      5:instanceof好处

1:可以判断对象是否是某一个类的实例

package oop01;

/*

 instanceof

 比较运算符

 检查是否是类的对象

    1:可以判断对象是否是某一个类的实例

    用法

    对象  instanceof ;

 案例

定义一个功能函数,根据传递进来的对象的做不同的事情

    如果是狗让其看家,如果是猫让其抓老鼠

1:定义动物类

2:定义狗类继承动物类

3:定义猫类继承动物类

4:定义功能根据传入的动物,执行具体的功能

 */

class Animal {

    String name;

    void eat() {

       System.out.println("吃东西");

    }

    void shout() {

       System.out.println("我是动物");

    }

}

class Dog extends Animal {

    void eat() {

       System.out.println("啃骨头");

    }

    void shout() {

       System.out.println("旺旺");

    }

}

class Cat extends Animal {

    void eat() {

       System.out.println("吃老鼠");

    }

    void shout() {

       System.out.println("喵喵");

    }

}

class Demo11 {

    public static void main(String[] args) {

       Demo11 d = new Demo11();

       // 对象 instanceof ;

       System.out.println(d instanceof Demo11);

        d.doSomething(new Dog());

       d.doSomething(new Cat());

    }

    // 定义一个功能函数,根据传递进来的对象的做不同的事情

    // 如果是狗让其看家,如果是猫让其抓老鼠

    // 对象 instanceof ;

    void doSomething(Animal a) {

       if (a instanceof Dog) {

           a.eat();

           a.shout();

           System.out.println("小狗看家");

       } else if (a instanceof Cat) {

           a.eat();

           a.shout();

           System.out.println("抓老鼠");

       }

    }

}

练习:

byte[] bs = new byte[] { 1, 2, 3 };

       int[] is = new int[] { 1, 2, 3 };

       String[] ss = new String[] { "jack", "lucy", "lili" };

       System.out.println(bs instanceof byte[]); // true

       System.out.println(is instanceof int[]); // true

       System.out.println(ss instanceof String[]); // true

       // System.out.println(bs instanceof int[]); // 不可转换的类型

1.9 final关键字

常量:

   1:定义静态方法求圆的面积

   2:定义静态方法求圆的周长

   3:发现方法中有重复的代码,就是PI,圆周率。

      1:如果需要提高计算精度,就需要修改每个方法中圆周率。

   4:描述一个变量

      1:方法都是静态的,静态只能访问静态,所以变量也定义为静态的。

public static double PI=3.14;

          1:如果定义为public后,新的问题,类名.PI=300; 改变了PI的值。

2:修改为private,修改为private后进行了封装,需要getset公共访问方法。

          3:现有的知识不能解决这样的问题。可以使用final

class Demo12 {

    public static final double PI = 3.14; // 静态常量

    public static double getArea(double r) {

       return PI * r * r;

    }

    public static double getLength(double r) {

       return PI * r * 2;

    }

    public static void main(String[] args) {

       // Demo12.PI=300; 无法为最终变量 PI 指定值

       System.out.println(Demo12.PI);

    }

}

使用final

1:final关键字主要用于修饰类、类成员、方法、以及方法的形参。

final修饰成员属性:

   1:说明该成员属性是常量,不能被修改。

      public static final double PI=3.14;

      1:public :访问权限最大

      2:static :内存中只有一份

      3:final  :是一个常量

      4:常量名大写

      5:必须初赋值。

   2:使用类名.成员。修改该成员的值,报错。--常量不能被修改

             1:基本数据类型,final使值不变

2:对象引用,final使其引用恒定不变,无法让其指向一个新的对象,但是对象自身却可以被修改。

             3:该关键字一般和static关键字结合使用

                1:常量可以优先加载,不必等到创建对象的时候再初始化。

             4:final和static可以互换位置

final修饰类:

   1:该类是最终类,不能被继承。

      1:将父类加final修饰,子类继承,就会报错。

2:查看api文档发现String类是final的。Integer类也是final的

      1:为了防止代码功能被重写

      2:该类没有必要进行扩展final修饰方法

final修饰方法

   1:该方法是最终方法,不能被重写

2:当一个类被继承,那么所有的非私有函数都将被继承,如果函数不想被子类继承并重写可以将该函数final修饰

   3:当一个类中的函数都被修饰为final时,可以将类定义为final的。

class Father2{

    final void eat(){

       System.out.println("eating....");

    }

}

class Son2 extends Father2{

    //该方法是最终方法,不能被重写

    void eat(){

       System.out.println("eating....");

    }

}

class Demo12 {

    public static void main(String[] args) {

       // Demo12.PI=300; 无法为最终变量 PI 指定值

       System.out.println(Demo12.PI);

       Son2 s=new Son2();

       s.eat();

    }

final关键字修饰形参

   1:当形参被修饰为final,那么该形参所属的方法中不能被篡改。

2: 项目中主要用于一些只用来遍历未知数据的函数。将未知变量声明为final的。增强数据的安全性。

class Demo14 {

    public static void main(String[] args) {

       System.out.println();

       String[] arr = { "think in java", "java就业教程", "java核心技术" };

       print(arr);

    }

    // 该方法,打印书名。

    public static void print(final String[] arr) {

       //arr = null; ,无法重新赋值

       for (int x = 0; x < arr.length; x++) {

           System.out.println(arr[x]);

       }

    }

}

思考

   为什么子类一定要访问父类的构造函数呢

1:子类继承了父类的属性,如果要使用父类的属性必须初始化,创建子类对象,必须先初始化父类属性

             必须调用父类的构造方法。

          2:为什么调用父类无参的构造函数

设计java语言之时,只知道编译器会默认添加无参的构造函数,有参的无法确定。

             但是可以通过super关键字显式调用父类指定构造函数。

          3:为什么super()this()语句要放在构造函数的第一行

             子类可能会用到父类的属性,所以必须先初始化父类。

2、多态

2.1 抽象类

为什么使用抽象类

   1:定义Dog类

      有颜色属性和叫的方法

   2:定义Bird类

      有颜色属性和叫的方法

   3:定义其父类Animal

      1:抽取共性颜色属性和叫的方法

          1:颜色的属性可以使用默认初始化值。

          2:叫的方法在父类中如何定义?

             1:狗是旺旺

             2:鸟是叽叽喳喳

             3:可以将父类的方法定义为狗叫让鸟继承父类重写叫的方法

                1:鸟怎么确定是否要重写父类方法。

2:不重写,编译和运行都没有问题,只是执行鸟叫的方法就会出现狗叫

             4:父类的方法很难确定。

class Animal {

    String color;

     void shout(){

        //如何定义呢?是旺旺还是叽叽喳喳?

     }

}

class Dog extends Animal {

    void shout() {

       System.out.println("旺旺");

    }

}

class Bird extends Animal {

    void shout() {

       System.out.println("叽叽喳喳");

    }

}

          2:使用abstract

      4:抽象类

1:当描述一个类的时候,如果不能确定功能函数如何定义,那么该类就可以定义为抽象类,功能函数应该描述为抽象函数。

      5:抽象类的实现方式

          1:定义animal类

                1:定义叫的方法,无法确定方法体,不写方法体

                   1:public void shout (); 编译失败 

                2:根据提示在shout的方法加入abstract修饰

                   1:编译失败,有新的提示

                3:根据提示将类加入abstract修饰

                   1:编译通过

abstract class Animal {

    String color;

    abstract void shout();

}

class Dog extends Animal {

    void shout() {

       System.out.println("旺旺");

    }

}

class Bird extends Animal {

    void shout() {

       System.out.println("叽叽喳喳");

    }

}

      6:抽象类的特点

          1:有抽象函数的类,该类一定是抽象类。

          2:抽象类中不一定要有抽象函数。

          3:抽象类不能使用new创建对象

             1:创建对象就是为了使用对象的功能,但是抽象类的方法,没有方法体。调用抽象方法没有意义.

          4:抽象类主要为了提高代码的复用性,让子类继承来使用。

          5:编译器强制子类实现抽象类父类的未实现的方法。

             1:可以不实现,前提是子类的也要声明为抽象的。

      7:抽象的优点

          1:提高代码复用性

          2:强制子类实现父类中没有实现的功能

          3:提高代码的扩展性,便于后期的代码维护

             例如:

      8:抽象类不能创建对象,那么抽象类中是否有构造函数?

1:抽象类中一定有构造函数。主要为了初始化抽象类中的属性。通常由子类构造函数中通过super来访问。

      9:final和abstract是否可以同时修饰一个类? 

          一定不能同时修饰。

abstract class Animal {

    String name;

    // 抽象类可以有构造函数

    Animal() {

    }

    Animal(String name) {

       this.name = name;

    }

    abstract void shout();

}

class Dog extends Animal {

    Dog() {

    }

    Dog(String name) {

       super(name);

    }

    void shout() {

       System.out.println("旺旺");

    }

}

class Demo3 {

    public static void main(String[] args) {

       // 抽象类不能创建对象

       // Animal a=new Animal();

       Dog d = new Dog("旺财");

       System.out.println();

    }

}

2:抽象练习

1:定义抽象类MyShape(图形)

      1:定义抽象方法获取图形的长度和面积

2:定义子类Rect继承父类MyShape  

      1:定义自身特有的长和宽(成员变量)  width height;

      2:实现父类未实现的函数。

   3:定义子类 Circle实现父类MyShape

      1:定义自身特有的半径和圆周率(使用常量)

      2:实现父类为实现的方法。

/*

 }

 2:抽象练习

 1:定义抽象类MyShape(图形)

 1:定义抽象方法获取图形的长度和面积

 2:定义子类Rect继承父类MyShape  

 1:定义自身特有的长和宽(成员变量)  width height;

 2:实现父类未实现的函数。

 3:定义子类 Circle实现父类MyShape

 1:定义自身特有的半径和圆周率(使用常量)

 2:实现父类为实现的方法。

 */

abstract class MyShape {

    abstract double getLen();

    abstract double getArea();

}

class Rect extends MyShape {

    double width;

    double height;

    Rect() {

    }

    Rect(double width, double height) {

       this.width = width;

       this.height = height;

    }

    double getLen() {

       return 2 * (width + height);

    }

    double getArea() {

       return width * height;

    }

}

class Circle extends MyShape {

    double r;

    public static final double PI = 3.14;

    Circle() {

    }

    Circle(double r) {

       this.r = r;

    }

    double getLen() {

       return 2 * PI * r;

    }

    double getArea() {

       return PI * r * r;

    }

}

class Demo4 {

    public static void main(String[] args) {

       Rect r = new Rect(5, 5);

       System.out.println(r.getLen());

       System.out.println(r.getArea());

       System.out.println();

       Circle c = new Circle(5);

       System.out.println(c.getLen());

       System.out.println(c.getArea());

    }

}

2.2 java数值交换

   1:基本数据类型交换

      1:定义changeBase方法,定义形参,接收2个int变量,定义第三方变量交换。

      2:main方法定义2个int类型变量,调用changeBase方法进行交换。

      3:分析内存

          1:main方法的变量在栈内存

          2:main方法调用changeBase方法,方法中的变量也在栈内存

          3:changeBase方法运行完释放。

          4:main方法中的2个变量没有交换。

   2:数组数据类型值交换

      1:定义changeArr方法,接收int数组,接收指定角标互换。

      2:内存分析

1:main方法进栈,定义数组类型变量,同时堆内存开辟空间,数组变量持有该引用。

          2:main方法加载changeArr方法。

          3:changeArr方法释放,数组完成交换。

      3:数组数据类型值可以交换

   3:类类型进行值交换

      1:定义changeClass方法,接收对象,接收成员变量新值

      2:内存分析

          1:main方法进栈,定义类类型变量,堆内存开辟空间,持有该引用。

          2:main方法调用changeClass方法

          3:changeClass方法运行完毕,释放。

          4:实例变量的值改变。

      3:对象所维护的实例变量的值改变。

   4:总结

      1:基本类型不能交换

      2:所有引用类型可以交换(数组,类)但是String不可以

/*

 数值交换

 基本数据类型交换

 数组数据类型值交换

 类类型进行值交换

 4:总结

 1:基本类型不能交换

 2:所有引用类型可以交换(数组,类)除了String

 */

import java.util.Arrays;

class Demo5 {

    int x = 1;

    public static void main(String[] args) {

       int x = 1;

       int y = 3;

       System.out.println("调换前:" + x + ":" + y);

       changeBase(x, y);

       System.out.println("调换后:" + x + ":" + y);

       System.out.println("array.....");

       int[] arr = { 1, 2, 3, 4, 5 };

       System.out.println("调换前" + Arrays.toString(arr));

       changeArr(arr, x, y);

       System.out.println("调换后" + Arrays.toString(arr));

       System.out.println("class....");

       Demo5 d = new Demo5();

       System.out.println("修改前:" + d.x);

       changeClass(d, 5);

       System.out.println("修改后:" + d.x);

    }

    // 1:定义changeBase方法,定义形参,接收2int变量,定义第三方变量交换。

    public static void changeBase(int x, int y) {

       int temp = x;

       x = y;

       y = temp;

       System.out.println("调换后:" + x + ":" + y);

    }

    // 2:定义changeArr方法,接收int数组,接收指定角标互换。

    public static void changeArr(int[] arr, int x, int y) {

       int temp = arr[x];

       arr[x] = arr[y];

       arr[y] = temp;

    }

    // 类类型进行值交换

    // 1:定义changeClass方法,接收对象,接收成员变量新值

    public static void changeClass(Demo5 d, int y) {

       d.x = y;

    }

}

2.3 接口

   1:没有接口会出现什么问题

      1:定义铅笔类(Pencil),名字,写功能

      2:定义带橡皮的铅笔(PencilWithEraser),写功能,擦除功能

          1:带橡皮的铅笔is a 铅笔有继承关系,继承了铅笔

      3:定义橡皮类(Eraser),擦除功能

          1:带橡皮的铅笔有橡皮也可以继承橡皮,但java只支持单继承

      4:由于java单继承的限制,无法解决现实中这类问题,那么就有了接口

5:发现带橡皮的铅笔是铅笔只是进行了功能加强,具备了擦除功能,可以把橡皮定义为接口

class Pencil {

    String name;

    Pencil() {

    }

    Pencil(String name) {

       this.name = name;

    }

    void write() {

       System.out.println("写字");

    }

}

interface Eraser {

    public static final String color = "白色";

    public abstract void clean();

}

// 1:带橡皮的铅笔类继承铅笔类实现橡皮接口

class PencilWithEraser extends Pencil implements Eraser {

    PencilWithEraser() {

    }

    PencilWithEraser(String name) {

       super(name);

    }

    void write() {

       System.out.println(name + ":考试专用");

    }

    public void clean() {

       System.out.println(super.name + ":带橡皮的铅笔,就是好用");

    }

}

class Demo6 {

    public static void main(String[] args) {

       PencilWithEraser pe = new PencilWithEraser("中华2B");

       pe.write();

       pe.clean();

       System.out.println(pe.color);

       System.out.println(PencilWithEraser.color);

    }

}

   2:接口是什么

      1:接口usb接口,usb接口主要用于扩展笔记本的功能。

          1:笔记本硬盘空间不够,使用usb移动硬盘解决问题。

      2:java中接口:扩展java定义的类的功能,解决了java单继承的缺陷。

      3:接口定义

          interface关键字,用于定义一个接口类。

          interface 接口名{

          }

          1:描述接口的属性(成员变量)

          2:描述接口的行为(成员方法)

      4:使用接口

          implements 关键字可以使子类实现指定的多个接口

      5:接口特点

          1:接口中定义的所有属性默认是public static final的,即静态常量

             1:即使静态常量,那么定义的时候必须赋值。

          2:接口中定义的方法不能有方法体,接口中定义的方法默认添加public abstract

             1:即接口不能定义实现方法

             2:不添加public abstract也可以,默认添加,但不便于阅读

          3:接口不可以创建对象,因为接口有抽象方法。

             1:new 接口编译报错。

4:接口要被子类实现,子类对接口中的抽象方法全部覆盖后子类才可以实例化,否则子类是一个抽象类。

          5:接口可以被类多实现

interface A{

}

interface B{

}

interface C{

}

class D implements A,B,C{

}

6:如果实现类要访问接口中的成员,不能使用super关键字,因为两者没有显示的继承关系,可以使用接口名访问。

      6:接口中是否有构造函数

          1:一定没有

             1:构造函数用于初始化非静态成员变量,接口中变量是静态常量。

      7:接口练习

          1:带橡皮的铅笔类继承铅笔类实现橡皮接口

      8:接口使用细节

          1:一个实现类可以同时实现多个接口

1:接口A,接口B ,实现类C可以同时实现A和B,例如implements A,B

          2:接口之间可以进行多继承。

1:接口A,接口B,接口C ,接口C 可以同时继承A,和B,中间用逗号隔开。

             2:接口与接口之间不能实现

3:定义接口A,B,C 类D实现 A,B ,C 创建D类对象,使用instanceof 比较A,B,C 全是true

1:如果一个实现类实现了很多接口,那么这个实现类的对象是这些接口的对象。

          5:接口A,接口B 都定义了同名方法,实现类该如何实现。

             1:只用实现一个方法即可。

             1:接口中的同名方法,有重复代码,可以通过接口的继承来解决

      9:抽象类是强迫子类重写父类方法,接口是增强实现类功能。

2.4 多态

   1:什么是多态

      一个对象的多种状态

      (老师)(员工)(儿子)

      教师 a =make;

      员工 b= make;

   2:多态体现

      1:Father类

          1:非静态成员变量x

          2:静态成员变量y

          3:非静态方法eat,方法体输出父类信息

          4:静态方法speak();方法体输出父类信息

      2:Son类

          1:非静态成员变量x

          2:静态成员变量y

          3:非静态方法eat,方法体输出子类信息

          4:静态方法speak();方法体输出子类信息

class Father {

    int x = 1;

    static int y = 2;

    void eat() {

       System.out.println("开吃");

    }

    static void speak() {

       System.out.println("小头爸爸");

    }

}

class Son extends Father {

    int x = 3;

    static int y = 4;

    void eat() {

       System.out.println("大头儿子很能吃");

    }

    static void speak() {

       System.out.println("大头儿子。");

    }

}

class Demo10 {

    public static void main(String[] args) {

       Father f = new Son(); // 父类引用指向了子类对象。

       System.out.println(f.x); // 1

       System.out.println(f.y); // 2

       f.eat(); // 输出的是子类的。

       f.speak(); // 输出的是父类

    }

}

      3:Son类继承父类

          1:创建Father f=new Son();

             1:这就是父类引用指向了子类对象。

             2:问f.x=?(非静态)

             3:问f.y=?(静态)

             4:问f.eat()输出的是子类还是父类信息?(非静态)

             5:问f.speak()输出的是子类还是父类信息?(静态)

      4:总结

1:当父类和子类具有相同的非静态成员变量,那么在多态下访问的是父类的成员变量

2:当父类和子类具有相同的静态成员变量,那么在多态下访问的是父类的静态成员变量

          所以:父类和子类有相同的成员变量,多态下访问的是父类的成员变量。

3:当父类和子类具有相同的非静态方法(就是子类重写父类方法),多态下访问的是子类的成员方法。

4:当父类和子类具有相同的静态方法(就是子类重写父类静态方法),多态下访问的是父类的静态方法。

   2:多态体现

      1:父类引用变量指向了子类的对象

      2:父类引用也可以接受自己的子类对象

3:多态前提

   1:类与类之间有关系,继承或者实现

4:多态弊端

   1:提高扩展性,但是只能使用父类引用指向父类成员。

5:多态特点

   非静态

1:编译时期,参考引用型变量所属的类是否有调用的方法,如果有编译通过。没有编译失败

   2:运行时期,参考对象所属类中是否有调用的方法。

   3:总之成员函数在多态调用时,编译看左边,运行看右边。

   在多态中,成员变量的特点,无论编译和运行参考左边(引用型变量所属的类)。

   在多态中,静态成员函数特点,无论编译和运行都参考左边

6:多态练习

   1:多态可以作为形参,接受范围更广的对象,避免函数重载过度使用。

      1:定义功能(函数),该函数可以输出任何图形的面积和周长.根据输出任何图形的面积和周长。

          1:定义抽象类abstract MyShape

             1:定义抽象方法public abstract double getArea();

            2:定义抽象方法public abstract double getLen();

      2:定义Rect类继承MyShape

          1:定义长和宽成员变量,double width height;

          2:无参构造,有参构造。

          3:实现父类方法。

      3:定义Cricle类继承MyShape

          1:定义半径成员变量,和PI常量

          2:无参构造,有参构造

          3:实现父类方法。

      4:定义静态方法计算任意图形的面积和周长

          1:未知内容参与运算,不能确定用户传入何种图形,使用多态。

             1:形参定义为 MyShape my

          2:调用计算面积方法,和计算周长方法。并打印

             2:使用多态特性,子类重写了父类非静态方法,会执行子类的方法。

/*

多态练习

    1:多态可以作为形参,接受范围更广的对象,避免函数重载过度使用。

       1:定义功能,根据输出任何图形的面积和周长。

       子类重写了父类的抽象方法,多态下,会执行子类的非静态方法。

    2:多态可以作为返回值类型。

       获取任意一辆车对象

    3:抽象类和接口都可以作为多态中的父类引用类型。

*/

abstract class MyShape{

    public abstract double getArea();

    public abstract double getLen();

}

class  Rect extends MyShape{

    double width ;

    double height;

    Rect(){

    }

    Rect(double width ,double height){

       this.width=width;

       this.height=height;

    }

    public double getArea(){

       return width*height;

    }

    public  double getLen(){

       return 2*(width+height);

    }

}

class Circle extends MyShape{

     double r;

     public static final double PI=3.14;

     Circle(){

     }

     

    Circle(double r){

        this.r=r;

     }

    public double getLen(){

        return 2*PI*r;

     }

   

    public double getArea(){

        return PI*r*r;

     }

}

class Demo11{

    public static void main(String[] args){

       System.out.println();

       print(new Rect(3,4)); //MyShape m =new Rect(3,4);

       print(new Circle(3));

        }

       

        //根据用户传入的图形对象,计算出该图形的面积和周长

        //1:多态可以作为形参,接受范围更广的对象,避免函数重载过度使用。

        public static void print(MyShape m){ 

           System.out.println(m.getLen());

           System.out.println(m.getArea());

        }      

}

   2:多态可以作为返回值类型。

     定义一个汽车工厂类,获取任意一辆车对象,该工厂生产的汽车随机.

          1:定义汽车类,有名字和颜色,提供有参和无参构造,有运行的行为。

2:定义Bmw类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。

3:定义Benz类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。

4:定义Bsj类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。

          5:定义静态方法,汽车工厂,随机生产汽车。使用多态定义方法返回值类型。

             1:使用(int)Math.round(Math.random()*2); 生成0-2之间随机数。

             2:使用if else 判断,指定,0,1,2 new 不同汽车 并返回。

          6:调用该方法,发现多态的好处。

*

 2:多态可以作为返回值类型。

 获取任意一辆车对象

 1:定义汽车类,有名字和颜色,提供有参和无参构造,有运行的行为。

 2:定义Bmw类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。

 3:定义Benz类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。

 4:定义Bsj类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。

 5:定义静态方法,汽车工厂,随机生产汽车。使用多态定义方法返回值类型。

 1:使用(int)Math.round(Math.random()*2); 生成0-2之间随机数。

 Math

 2:使用if else 判断,指定,0,1,2 new 不同汽车 并返回。

 6:调用该方法,发现多态的好处。

 */

class Car {

    String name;

    String color;

    Car() {

    }

    Car(String name, String color) {

       this.name = name;

       this.color = color;

    }

    void run() {

       System.out.println("跑跑。。。。");

    }

}

class Bmw extends Car {

    Bmw() {

    }

    Bmw(String name, String color) {

       super(name, color);

    }

    void run() {

       System.out.println("宝马很拉风。。。。");

    }

}

class Benz extends Car {

    Benz() {

    }

    Benz(String name, String color) {

       super(name, color);

    }

    void run() {

       System.out.println("奔驰商务首选。。。。");

    }

}

class Bsj extends Car {

    Bsj() {

    }

    Bsj(String name, String color) {

       super(name, color);

    }

    void run() {

       System.out.println("泡妞首选。。。。");

    }

}

class Demo12 {

    public static void main(String[] args) {

       int x = 0;

       while (x < 100) {

           Car c = CarFactory();

           c.run();

           x++;

       }

    }

    // 定义静态方法,汽车工厂,随机生产汽车。使用多态定义方法返回值类型。

    // 使用随机数,0.1.2 if 0 bsj 1 bmw 2 bc

    public static Car CarFactory() {

       int x = (int) Math.round(Math.random() * 2);

       if (0 == x) {

           return new Bmw("宝马x6", "红色");

       } else if (1 == x) {

           return new Benz("奔驰", "黑色");

       } else if (2 == x) {

           return new Bsj("保时捷", "棕色");

       } else {

           return new Benz("Smart", "红色");

       }

    }

}

3:抽象类和接口都可以作为多态中的父类引用类型。

          1:sun Arrays

6:多态之类型转型

   1:案例定义Father类

      1:定义method1和method2方法

   2:定义Son类继承Father类

      1:定义method1(重写父类method1)和method2方法,同时子类定义自身特有的method3方法.

   3:创建Father f=new Son();

      1: f.method1() 调用的子类或者父类?

      2: f.method2() 编译和运行是否通过?

      3: f.method3() 编译和运行是否通过?(编译报错)

   4:如何在多态下,使用父类引用调用子类特有方法。

需要使用类类型装换.

      1:基本类型转换:

          1:自动:小->大

          2:强制:大->小

      2:类类型转换

          前提:继承,或者实现.也就是必须有关系

          1:自动:子类转父类

          2:强转:父类转子类

          3:类型转换

             1:Son s=(Son)f

             2:s.method3();

/*

 如何在多态下,使用父类引用调用子类特有方法。

 1:基本类型转换:

 1:自动:小->    int x=1 double d=x;

 2:强制:大->    int y=(int)d;

 2:类类型转换

 前提:继承,必须有关系

 1:自动:子类转父类  Father f=new Son();

 2:强转:父类转子类  Son s=(Son)f;   

 1:类型转换

 1Son s=(Son)f

 2s.method3();

 */

class Father {

    void method1() {

       System.out.println("这是父类1");

    }

    void method2() {

       System.out.println("这是父类2");

    }

}

class Son extends Father {

    void method1() {

       System.out.println("这是子类1");

    }

    void method3() {

       System.out.println("这是子类3");

    }

}

class Demo14 {

    public static void main(String[] args) {

       Father f = new Son();

       f.method1(); // 这是子类1

       f.method2(); // 这是父类2

       // f.method3(); //编译报错。

       // 多态弊端,只能使用父类引用指向父类成员。

       // 类类型转换

       Son s = (Son) f;

       s.method3();

       System.out.println();

    }

}

5:案例:测试类类型转换

本案例建立动物的继承体系.例如有父类Animal,子类Dog Fish等.每个子类有自己特有的行为.

请分别尝试,将Animal强制类型转换为Dog.

          Dog强制类型转换为Fish,并观察发生了什么

   1:定义Animal类颜色成员变量,无参构造,有参构造,run方法

2:定义Dog类,继承Animal,定义无参构造,有参构造(使用super调用父类有参构造),Dog的特有方法ProtectHome

3:定义Fish类,继承Animal,定义无参构造,有参构造(使用super调用父类有参构造),Fish特有方法swim

4:定义Bird类,继承Animal,定义无参构造,有参构造(使用super调用父类有参构造),Bird特有方法fly

   5:使用多态,Animal a=new Dog();

   6:调用Dog的特有方法,ProtectHome

      1:类类型转换,Dog d=(Dog)a;

      2:d.protectHome

   7:非多态

      1:Animal a=new Animal();

      2:类类型转换

          Dog d=(Dog)a; 

          d.protectHome();

      3:编译通过,运行出现异常

          1:ClassCastException

   8:多态例外

          1:Animal  a=new Dog();

          2:类类型转换

             1:Fish f=(Fish)a;

             2:f.fish();

             3:编译通过,运行异常

                1:ClassCastException

总结: 虽然是存在类与类之间继承关系,但是只有子类和父类之间有关系.但是鸟不能转为狗,狗不能转为鱼,他们之间没有关系。

class Animal {

    String color;

    Animal() {

    }

    Animal(String color) {

       this.color = color;

    }

    void run() {

       System.out.println("跑跑");

    }

}

class Dog extends Animal {

    Dog() {

    }

    Dog(String color) {

       super(color);

    }

    void run() {

       System.out.println("狗儿跑跑");

    }

    void protectHome() {

       System.out.println("旺旺,看家");

    }

}

class Fish extends Animal {

    Fish() {

    }

    Fish(String color) {

       super(color);

    }

    void run() {

       System.out.println("鱼儿水中游");

    }

    void swim() {

       System.out.println("鱼儿游泳");

    }

}

class Demo15 {

    public static void main(String[] args) {

       Animal ani = new Dog();

       // ani.protectHome();

       // 正常转换

       Dog d = (Dog) ani;

       d.protectHome();

       // 多态例外

       Animal an = new Animal();

       // ClassCastException

       // Dog d=(Dog)an

       // 多态例外

       Animal dog = new Dog();

       // ClassCastException

       // Fish f = (Fish) dog;

    }

}

6:案例2

1:定义一功能,接收用户传入动物,根据用于传入的具体动物,执行该动物特有的方法

   2:使用多态,方法形参,不能确定用户传入的是那种动物

   3:使用instanceof 关键字,判断具体是何种动物,

   4:类转换,执行该动物的特有方法。

package oop04;

/*

 案例2

 1:定义一功能,接收用户传入动物,根据用于传入的具体动物,执行该动物特有的方法

 2:使用多态,方法形参,不能确定用户传入的是那种动物

 3:使用instanceof 关键字,判断具体是何种动物,

 4:类转换,执行该动物的特有方法。

 */

class Animal {

    String color;

    Animal() {

    }

    Animal(String color) {

       this.color = color;

    }

    void run() {

       System.out.println("跑跑");

    }

}

class Dog extends Animal {

    Dog() {

    }

    Dog(String color) {

       super(color);

    }

    void run() {

       System.out.println("狗儿跑跑");

    }

    void protectHome() {

       System.out.println("旺旺,看家");

    }

}

class Fish extends Animal {

    Fish() {

    }

    Fish(String color) {

       super(color);

    }

    void run() {

       System.out.println("鱼儿水中游");

    }

    void swim() {

       System.out.println("鱼儿游泳");

    }

}

class Bird extends Animal {

    Bird() {

    }

    Bird(String color) {

       super(color);

    }

    void run() {

       System.out.println("鸟儿空中飞");

    }

    void fly() {

       System.out.println("我是一只小小鸟。。。。");

    }

}

class Demo16 {

    public static void main(String[] args) {

       System.out.println();

       doSomething(new Dog());

       doSomething(new Bird());

       doSomething(new Fish());

    }

    // 定义一功能,接收用户传入动物,根据用于传入的具体动物,执行该动物特有的方法

    public static void doSomething(Animal a) {

       if (a instanceof Dog) {

           Dog d = (Dog) a;

           d.protectHome();

       } else if (a instanceof Fish) {

           Fish f = (Fish) a;

           f.swim();

       } else if (a instanceof Bird) {

           Bird b = (Bird) a;

           b.fly();

       } else {

           System.out.println("over");

       }

    }

}

2.5 内部类  

inner class nested class

定义在类中的类

快速体验:

Outer类中有Inner类 Inner类就是内部类

class Outer {

    private int x = 1;

    // 成员内部类

    private class Inner {

       int y = 2;

       int x = 3;

       void innerPrint() {

           System.out.println("内部类" + y); // 2

           System.out.println(this.x); // 3

       }

    }

    void outPrint() {

       System.out.println("外部类" + x);

       // 外部类的成员方法上,创建成员内部类对象。

       Inner n = new Inner();

       n.innerPrint();

    }

}

class Demo1 {

    public static void main(String[] args) {

       Outer out = new Outer();

       out.outPrint();

    }

}

内部类分类

   成员内部类,静态内部类(嵌套类),匿名内部类

     

2.5.1 成员内部类

1:成员内部类和成员变量和成员方法是并列的。

   2:成员内部类也是类,也可以定义成员变量和成员方法 

内部类和外部类的相互访问

   在外部类的成员函数中创建内部类的对象,直接访问内部类的方法

案例

1:创建Outer类,是外部类,定义成员变量(例如 int x)和成员方法(例如:print方法)。

1:在Outer中,定义名字为Inner的内部类,定义成员变量(int y)和成员方法(例如innerPrint)。

2:创建内部类对象

3:外部类中调用内部类的成员方法

1:Outer类的print方法,在该方法中创建Inner对象,并调用Inner的方法innerPrint

4:内部类可以访问外部类的成员

1:外部类成员变量,在内部类中可以直接访问

5:内部类可以直接访问外部类的成员。

6:外部类和内部类定义了同名的成员变量(例如 int x=1;)

      1:在内部类中,先找内部类中,没有找到再找外部类的。

         2:如果内部类一定要访问外部类的成员

             1:使用this 是Inner类的对象

             2:使用this.Outer可以访问到外部类的成员变量。

其他类中直接创建内部类对象

   Outer.Inner in=new Outer().new Inner();

看到这里其实就应该明白,内部类对象持有有一个所在外部类对象引用.所以在创建内部类对象时,要先创建外部类对象.

class Outer {

    private int x = 1;

    // 成员内部类

    class Inner {

       int y = 2;

       int x = 3;

       void innerPrint() {

           System.out.println("内部类" + y); // 2

           System.out.println(this.x); // 3

           System.out.println(Outer.this.x);

       }

    }

    void outPrint() {

       System.out.println("外部类" + x);

       // 外部类的成员方法上,创建成员内部类对象。

       Inner n = new Inner();

       n.innerPrint();

    }

}

class Demo1 {

    public static void main(String[] args) {

       Outer out = new Outer();

       out.outPrint();

       // 成员内部类创建方式

       Outer.Inner in = new Outer().new Inner();

       in.innerPrint();

    }

}

   2:为什么定义内部类

      1:成员内部类作为外部类的成员,可以直接访问外部类的所有成员。

   3:成员内部类被private修饰

      1:外部类的成员变量和成员方法都可以被private修饰,成员内部类是否可以。

      2:案例

          1:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())

          2:内部类Inner,定义成员变量和成员方法(例如int x,interPrint())

          3:Inner被private修饰

4:在Outer 类的outerPrint()方法中创建Inner 对象,调用Inner 的interPrint()方法

             1:编译通过,运行没有问题。

          5:在其他类中创建内部类Inner对象

             1:编译失败

      3:被private修饰的成员内部类只能在内部类所在的外部类中创建和访问。

class Outer {

    private int x = 1;

    // 成员内部类

    private class Inner {

       int y = 2;

       int x = 3;

       void innerPrint() {

           System.out.println("内部类" + y); // 2

           System.out.println(this.x); // 3

           System.out.println(Outer.this.x);

       }

    }

    void outPrint() {

       System.out.println("外部类" + x);

       // 外部类的成员方法上,创建成员内部类对象。

       Inner n = new Inner();

       n.innerPrint();

    }

}

class Demo1 {

    public static void main(String[] args) {

       Outer out = new Outer();

       out.outPrint();

       // private修饰的成员内部类只能在内部类所在的外部类中创建和访问。

       // Outer.Inner in = new Outer().new Inner();

       // in.innerPrint();

    }

}

2.5.2 嵌套类(静态内部类)

成员内部类被static修饰

      1:案例

          1:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())

          2:内部类Inner,定义成员变量和成员方法(例如int x,interPrint())

          3:内部类的成员变量使用static修饰

             1:编译失败,提示内部类不能有静态声明

          4:将内部类Inner使用static修饰

5:在Outer 类的outerPrint()方法中创建Inner 对象,调用Inner 的interPrint()方法

             1:编译通过,运行没有问题。

          6:在其他类中创建静态内部类Inner对象

             1:编译提示,限定的新静态类。

             2:Outer.Inner in=new Outer.Inner();

      2:如果成员内部类包含静态成员,java规定内部类必须声明为静态的。

      3:访问形式

          1:Outer.Inner in=new Outer.Inner();

注意: 很显然创建静态内部类对象不需要外部类的对象.但是静态内部类不可以访问外部类的非静态成员.(静态不能访问非静态)可以将静态类理解为所在外部类的成员.可以直接通过类名访问静态类.

/*

 成员内部类static修饰

 如果成员内部类包含静态成员,java规定内部类必须声明为静态的。

 访问形式

 1Outer.Inner in=new Outer.Inner();

 */

class Outer {

    int x = 2;

    static int y = 1;

    // 成员内部类被static修饰

    static class Inner {

       int y = 2;

       int x = 3;

       static int z = 5; // 内部类有静态成员,类也需要是static

       void innerPrint() {

           System.out.println("内部类" + y); // 2

           System.out.println(this.x); // 3

           System.out.println(z);

       }

    }

    void outPrint() {

       System.out.println("外部类" + x);

       Inner n = new Inner();

       n.innerPrint();

    }

}

class Demo2 {

    public static void main(String[] args) {

       Outer out = new Outer();

       // 静态内部类创建方式

       Outer.Inner in = new Outer.Inner();

       in.innerPrint();

    }

}

2.5.3 局部内部类

      1:案例定义局部内部类

      2:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())

      3:在outerPrint()方法中定义一个类(例如LocalHost),该类有成员变量(例如int y),有方法interPrint();

      4:LocalHost类就叫做局部内部类,和局部变量int x是并列的。

      5:创建局部内部类对象(LocalHost类的对象)

          1:在outerPrint()方法中创建LocalHost类的对象并调用interPrint方法。

          2:编译通过,正常运行。

      6:局部内部类访问所在方法的局部变量x

          1:在outerPrint()方法中创建LocalHost类的对象并调用interPrint方法。

2:局部内部类(LocalHost类)的对象调用interPrint方法访问outerPrint方法中的局部变量x

             1:编译报错内部类访问局部变量,需要声明为最终类型。

          3:局部内部类访问所在方法的局部变量,该变量需要是final的。

      7:为什么加final

          1:生命周期

注意:方法的局部变量位于栈上,只存在于该方法的声明周期内,随着方法的运行而存在,方法的消失而消失.当方法结束时,栈内存会释放,变量消失.但是即使方法运行完毕,在该方法中创建的内部类对象仍然可能存活于堆内存中(例如将局部内部类的引用传递到其他代码中).由于生命周期的不同,所以内部类对象不能使用它们,如果一定要使用就需要将局部变量声明为final的.

注意: 局部内部类对象只能在该类所在的方法中实例化.

class Outer {

    int x = 2;

    static int y = 1;

    void outPrint() {

       // 局部内部类访问局部变量需要使用final修饰

       final int y = 2;

       class LocalClass {

           int x = 3;

           void innerPrint() {

              System.out.println("局部内部类" + x);

              System.out.println(y);

           }

       }

       System.out.println("外部类" + x);

       LocalClass lc = new LocalClass();

       lc.innerPrint();

    }

}

class Demo3 {

    public static void main(String[] args) {

       Outer out = new Outer();

       out.outPrint();

       System.out.println();

    }

}

2.5.4 匿名内部类

案例

2:定义接口MyInterface,成员变量(静态常量必须初始化)和方法print(默认public abstract)。

  2:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())

   3:内部类Inner 实现MyInterface接口,实现print方法。

   4:创建内部类对象

          在outerPrint方法中创建内部类对象,调用内部类的方法。

   5:此种案例的代码可以使用匿名内部类来实现。

   6:修改代码,使用匿名内部类

1:在outerPrint方法中 new MyInterface(){}; 花括号中需要实现接口的print方法

             2:接口不能new。

1:这里不是new接口,是在创建一个接口的实现类,只不过该接口的实现类没有名字。

             3:new MyInterface(){};  其实就是内部类的一种替换格式。

                1:调用print方法new MyInterface(){}.print();

      2:匿名内部类是内部类的简写格式

      3:使用匿名内部类的前提

          1:内部类要继承一个类或者实现接口

      4:格式 new 父类或接口(){子类内容}

      5:匿名内部类就是一个匿名子类对象

6:如果只调用一个方法可以new MyInterface(){}.print(); 形式调用,如果接口或者父类中有2个以上的方法,就需要起名字了。  

          1:MyInterface in=new MyInterface(){};

          2:in.print();

          3:多态的体现。

interface MyInterface {

    public static final int x = 3;

    public abstract void print();

    public abstract void run();

}

class Outer {

    int x = 2;

    static int y = 1;

    void outPrint() {

       final int y = 2;

       class LocalClass implements MyInterface {

           public void print() {

              System.out.println("局部内部类。。。。");

           }

           public void run() {

              System.out.println("跑跑。。。");

           }

       }

       LocalClass lc = new LocalClass();

       lc.print();

    }

}

class Demo4 {

    public static void main(String[] args) {

       new Outer().outPrint();

       System.out.println();

    }

}

interface MyInterface {

    public static final int x = 3;

    public abstract void print();

}

class Outer {

    int x = 2;

    static int y = 1;

    void outPrint() {

       final int y = 2;

       // class LocalClass implements MyInterface {

       // public void print() {

       // System.out.println("局部内部类。。。。");

       // }

        // }

       // }

       //

       // LocalClass lc = new LocalClass();

       // lc.print();

        //是对上述注释的简写

       new MyInterface() {

           public void print() {

              System.out.println("匿名内部类。。。。");

           }

       }.print();

    }

}

class Demo4 {

    public static void main(String[] args) {

       new Outer().outPrint();

       System.out.println();

    }

}

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

棉花糖老丫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值