JSE3-类特性-继承,覆盖,多态,static,内部类

30 篇文章 0 订阅

1.继承

1.1 概述

  • extends关键字用来继承
  • 子类拥有父类所有变量及方法
  • 单继承性: 父亲唯一,单继承性使代码更可靠。接口会提供多继承性的好处,而且没有(多继承的)缺点。
  • ==构造方法不能被继承==
  • ==初始化子类必先初始化父类==

1.2 super及调用父类构造方法

super用来引用最近的父类的成员变量或方法,且能==自动向上层父类追溯==;还可作为方法名表示父类构造方法[只能构造方法来用==super来调用父类构造方法==,普通方法不能调用]

1.3 ==this和super==

java中的this和super的用法总结
注意:无论是 super 还是 this,都必须放在构造方法的第一行;不能同时出现(因为都抢第1行!).

//this,super调用本类或父类构造方法.

class Person {  
    public static void prt(String s) {  
       System.out.println(s);  
    }  

    //构造方法(1)  
    Person() {  
       prt("父类·无参数构造方法: "+"A Person.");  
    }

    //构造方法(2)  
    Person(String name) {  
       prt("父类·含一个参数的构造方法: "+"A person's name is " + name);  
    }
}  



public class Chinese extends Person {  
    Chinese() {  
       super(); // 调用父类构造方法(1)  
       prt("子类·调用父类”无参数构造方法“: "+"A chinese coder.");  
    }  

    Chinese(String name) {  
       super(name);// 调用父类具有相同形参的构造方法(2)  
       prt("子类·调用父类”含一个参数的构造方法“: "+"his name is " + name);  
    }  

    Chinese(String name, int age) {  
       this(name);// 调用具有相同形参的构造方法(3)  
       prt("子类:调用子类具有相同形参的构造方法:his age is " + age);  
    }  

    public static void main(String[] args) {  
       Chinese cn = new Chinese();  
       cn = new Chinese("codersai");  
       cn = new Chinese("codersai", 18);  
    }  
}

执行结果如下:
父类·无参数构造方法: A Person.
子类·调用父类”无参数构造方法“: A chinese coder.

父类·含一个参数的构造方法: A person's name is codersai
子类·调用父类”含一个参数的构造方法“: his name is codersai

父类·含一个参数的构造方法: A person's name is codersai
子类·调用父类”含一个参数的构造方法“: his name is codersai
子类:调用子类具有相同形参的构造方法:his age is 18

如果一个构造方法,它既没有super(…)也没有this(…),编译器自动插入一个调用到父类构造方法中,而不带参数。

1.3 有继承的执行顺序

public class ExeSequenceChild extends ExeSequenceParent {       //第1行
    private String name = "childName";                          //第2行,8-1
    private int age = 10;                                       //第3行,8-2
    public ExeSequenceChild() {                                 //第4行,3
        age = 50;                                               //第5行,9
    }                                                           //第6行,10
    public static void main(String[] args) {                    //第7行,1
        ExeSequenceChild t = new ExeSequenceChild();            //第8行,2
        System.out.println(t.name + "的年龄是" + t.age + "岁"); //第9行,11
    }                                                           //第10行,12
}                                                               //第11行
class ExeSequenceParent {                                       //第12行
    private int num = 30;                                       //第13行,5
    public ExeSequenceParent() {                                //第14行,4
        System.out.println("现在初始化父类");                   //第15行,6
    }                                                           //第16行,7
    public void test() {                                        //第17行
        System.out.println("这是父类的test方法");               //第18行
    }                                                           //第19行
}                                                               //第20行
/*  上述类的基本运行顺序是:
(1) :先运行到第 7 行,这是程序的入口
(2) :然后运行到第 8 行,这里要 new 一个 ExeSequenceChild,就要调用 ExeSequenceChild 的构造方法
(3) :就运行到第 4 行,注意:初始化子类必先初始化父类
(4) :要先初始化父类,所以运行到第 14 行
(5) :然后是第 13 行,初始化一个类,必须先初始化它的属性
(6) :然后是第 15 行
(7) :然后是第 16 行,表示父类初始化完成
(8) :然后是回到子类,开始初始化属性,因此运行到第 2 行,然后是第 3 行
(9) :子类属性初始化完过后,才回到子类的构造方法,执行里面的代码,也就是第 5 行
(10) :然后是第 6 行,表示 new 一个 ExeSequenceChild 实例完成
(11) :然后回到 main 方法中执行第 9 行
(12) :然后是第 10 行
运行结果是:
现在初始化父类
childName的年龄是50岁
*/

2:方法的覆盖和重载

2.1 概述覆盖(Overridden Methods)

子类方法,其名称、返回类型及参数表正好与父类中方法的名称、返回类型及参数相匹配,则新方法被称做覆盖旧方法。

2.2:子类覆盖父类调用方法

package cn.htmlv5.bobshute.jse.javasihu.basic.classfeature3.extend;

class Parent {
    public  void foroverwide2(){
        System.out.println("person foroverwide2");
    }
}


package cn.htmlv5.bobshute.jse.javasihu.basic.classfeature3.extend;

public class Child extends Parent {

    public  void foroverwide2(){
        System.out.println("child foroverwide2");
    }

    public static void main(String[] args) {

        Child cn2 = new Child();
        Parent cn3 = new Child();
        Parent cn4 = new Parent();

        cn2.foroverwide2();
        cn3.foroverwide2();
        cn4.foroverwide2();
    }

}


运行结果如下:
child foroverwide2
child foroverwide2
person foroverwide2

==总结==:
编译时看数据类型,运行时看实际的对象类型(new 操作符后跟的构造方法是哪个类的)。一句话:new 谁就调用谁的方法.

2.3 ==覆盖方法的规则==

  • 覆盖方法的返回类型、方法名称、参数列表必须与它所覆盖的方法的相同。

  • 覆盖方法不能比它所覆盖的方法访问性差(即访问权限不允许缩小)。
    如:Parent中有public方法print,如果Child 继承后为pirvate 方法print;那么Parent p2 = new Child();如果p2.pirnt因为访问变小则无法访问. [编译时就报错]

  • 覆盖方法不能比它所覆盖的方法抛出更多的异常。

这些规则源自多态性的属性和 Java 编程语言必须保证“类型安全”的需要。

2.4 重载

2.4.1 重载概述

  • 在同一个 Java 类中(包含父类),如果出现了方法名称相同,而参数列表不同的情况就叫做重载。

  • 代码来调用这些方法中的一个方法时,便以其会根据提供的参数的类型来选择合适的方法。

  • 跟成员方法一样,构造方法也可以重载。

2.4.2 重载的规则

  • 方法名称必须相同

  • 参数列表必须不同(个数不同,或类型不同,或参数排列顺序不同)。

  • 参数变量名称不同或返回类型不同(编译报错)不是重载。

注意:调用语句的参数表必须有足够的不同,以至于允许区分出正确的方法被调用。 如,单精度类型 float 到双精度类型 double 可能被应用,但是这样会导致在某些条件下的混淆。

//返回类型不同,不是重载,会编译报错(提示方法已存在)
    String get1(){
            return "str";
        }
    Integer get1(){
        return 1;
    }

2.5 覆盖和重载区别

  • 方法覆盖:

在一个类中创建的方法与父类中方法的名字、返回类型和参数表相同,覆盖是针对两个类说的,而且必须是子类(或孙类,孙孙类等)覆盖掉父类的方法.

  • 重载方法:

在一个类(或父子类)中用相同的名字创建多个方法(每个方法的参数表不同)

3.多态

3.1:多态概述

多态是同一个行为具有多个不同表现形式或形态的能力,针对类(Employee e = new Manager()).

注意:方法没有多态的说法,严格说多态是类的特性。但是也有对方法说多态的, 方法覆盖称为动态多态,是一个运行时问题;方法重载称为静态多态,是一个编译时问题。

一个对象只有一个格式(是在构造时给它的),既然变量能指向不同格式的对象,那么变量就是多态性的。也就是说一个对象只有一种形式,但一个变量却有多种不同形式。
如:

父类Employee和子类Manager.
Employee e = new Manager()

//此时只能访问Employee的方法,如果需要强制转换
Manager m = (Manager)e;
//此时可以访问manger的放法

3.2:instanceof 运算符

强制转换前的判断用instanceof.

3.3:静态绑定与动态绑定

==Java中的静态绑定和动态绑定==,该文章讲的很清楚.
方法的静态绑定和动态绑定.

  • 静态绑定: 又称作早期绑定; 发生在编译时期;使用private或static或final修饰的变量或者方法时用静态绑定;静态绑定使用类信息来完成;重载(Overload)的方法使用静态绑定完成
  • 动态绑定: 亦称为后期绑定;发生在运行时期;虚方法(可以被子类重写的方法)则会根据运行时的对象进行动态绑定;动态绑定则需要使用对象信息来完成;重写(Override)的方法则使用动态绑定完成;运行时会比静态耗时.

3.3.1 举例

  • 重载
public class TestMain {
  public static void main(String[] args) {
      String str = new String();
      Caller caller = new Caller();
      caller.call(str);
  }

  static class Caller {
      public void call(Object obj) {
          System.out.println("an Object instance in Caller");
      }

      public void call(String str) {
          System.out.println("a String instance in in Caller");
      }
  }
}

//运行结果
a String instance in in Caller

上述代码中:call方法重载,1个入参为Object,1个入参为String。str是一个String对象,所以string入参的call方法会被调用。而这里的绑定就是在编译时期根据参数类型进行的静态绑定。
//以上文章中还有javap查看class的验证,编译后的class中已经指向了string的入参方法. 
  • 重写
public class TestMain {
  public static void main(String[] args) {
      String str = new String();
      Caller caller = new SubCaller();
      caller.call(str);
  }

  static class Caller {
      public void call(String str) {
          System.out.println("a String instance in Caller");
      }
  }

  static class SubCaller extends Caller {
      @Override
      public void call(String str) {
          System.out.println("a String instance in SubCaller");
      }
  }
}

//运行结果
a String instance in SubCaller

上述代码: Caller有call方法,SubCaller继承Caller并重写call。而我们:Caller caller = new SubCaller()。结果是调用了SubCaller的call方法实现,而非Caller的call方法。  这一结果的产生的原因是因为在运行时发生了动态绑定,在绑定过程中需要确定调用哪个版本的call方法实现。

//以上文章中还有javap查看class的验证,编译后的class中已经指向了string的入参方法. 
  • 重载+重写
public class TestMain {
  public static void main(String[] args) {
      String str = new String();
      Caller callerSub = new SubCaller();
      callerSub.call(str);
  }

  static class Caller {
      public void call(Object obj) {
          System.out.println("an Object instance in Caller");
      }

      public void call(String str) {
          System.out.println("a String instance in in Caller");
      }
  }

  static class SubCaller extends Caller {
      @Override
      public void call(Object obj) {
          System.out.println("an Object instance in SubCaller");
      }

      @Override
      public void call(String str) {
          System.out.println("a String instance in in SubCaller");
      }
  }
}
运行结果
a String instance in in SubCaller

4. static

4.1:static概述

  • static 修饰符能够与属性、方法和内部类一起使用,表示是“静态”的,又称作“类变量”(class variables)。

  • 类中的静态变量和静态方法用“类名”访问,可以不用new 对象来访问(new 对象也可以访问)。

  • 编译器在类装载(不论是否被用到)的时候创建一个(有且只有1个)静态变量的内存空间,所以即使多个实例也是共享一个内存空间。

4.2:static注意

  • 一个类的静态方法只能访问静态属性,不能访问类中的其它属性,静态方法不能够直接调用非静态方法

  • 静态方法中不存在当前对象,因而不能使用“this”,当然也不能使用”super”;

  • 静态方法不能被非静态方法覆盖;

  • 构造方法不允许声明为 static 的

4.3:静态初始器——静态代码块

  • 静态初始器(Static Initializer)是一个存在与类中方法外面的静态块。
  • 静态初始器仅仅在类装载的时候(第一次使用类的时候)执行一次。
  • 静态初始器的往往用来初始化静态的类属性。

4.4:静态 import

//jdk5之前,import导入类:
import static java.lang.Math;
double randomNum = Math.random();

//import可以直接导入类的静态方法
import static java.lang.Math.random;
double randomNum = random();

也可以用这种方式导入枚举.

5.final

5.1 概述

  • final表示终态,可以声明类、属性和方法,或引用类型(如:final FinalTest finalTest = new FinalTest();)

  • final 标记的类不能被继承(如: Java.lang.String),方法不能被子类重写,标记的变量(成员变量或局部变量)即成为常量,只能赋值一次。

    • final 标记的成员变量必须在声明的同时赋值。
    • final 标记的局部变量可以只声明不赋值,然后再进行一次性的赋值。
  • final 一般用于标记那些通用性的功能、实现方式或取值不能随意被改变的成分,以避免被误用,

  • final方法 final 的方法不能被覆盖,会静态绑定(3.3 静态绑定和动态绑定);被标记为 static 或 private 的方法被自动地加上final.
public class FinalTest {

    //public  final String str1;//不赋值初始值则编译报错
    public  final String str2= "";

    public void testMethod(){
        final String str3;
        str3="11";   //局部变量第一次赋值可以,
        //str3="22";   //局部变量第二次赋值则报错.
    }

    public static void main(String[] args) {
        //str2 = "11";//final的成员变量,不能赋值.
    }
}

6.==内部类==

6.1 内部类概述

内部类(Inner Classes): 在一个类(或方法、语句块)的内部定义另一个类,后者称为内部类,有时也称为嵌套类(Nested Classes)。内部类和外层封装它的类之间存在逻辑上的所属关系,一般只用在定义它的类或语句块之内,实现一些没有通用意义的功能逻辑,在外部引用它时必须给出完整的名称。

引入内部类的好处在于可使源代码更加清晰并减少类的命名冲突。内部类是一个有用的特征,因为它们允许将逻辑上同属性的类组合到一起,并在另一个类中控制一个类的可视性。

6.2:内部类特点

  • (1):内部类(嵌套类)可以体现逻辑上的从属关系。同时对于其他类可以控制内部类对外不可见等
  • (2):外部类的成员变量作用域是整个外部类,包括内部类。但外部类不能访问内部类的 private 成员
  • (3):逻辑上相关的类可以在一起,可以有效的实现信息隐藏。
  • (4):内部类可以直接访问外部类的成员。可以用此实现多继承!
  • (5):编译后,内部类也被编译为单独的类,不过名称为 outclass$inclass 的形式。

示例:

public class Outer {

    private int size;

    public class Inner{

        private int counter = 10;

        public void doStuff(){ size++; }

    }

    public static void main(String args[]){

        Outer outer=new Outer ();

        Inner inner= outer.new Inner();
        inner.doStuff();

        System.out.println(outer.size);
        System.out.println(inner.counter);

        //System.out.println(counter);  编译错误,外部类不能访问内部类的 private 变量

    }

}

6.3:内部类的分类

内部类按照使用上可以分为四种情形:

  • (1):类级:成员式,有 static 修饰

  • (2):对象级:成员式,普通,无 static 修饰

  • (3):本地内部类:局部式

  • (4):匿名级:局部式

6.4:成员式内部类

内部类可以作为外部类的成员,成员式内部类如同外部类的一个普通成员。
示例:

public class Outer1{

    private int size;

    public class Inner{
        public void dostuff(){
            size++;
        }
    }

    public void testTheInner(){

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

    }

}

6.4.1:成员式内部类的基本规则

  • (1):可以有各种修饰符,可以用 4 种权限、static、final、abstract 定义(这点和普通的类是不同的);

  • (2):若有 static 限定,就为类级,否则为对象级。类级可以通过外部类直接访问;对象级需要先生成外部的对象后才能访问。

  • (3):内外部类不能同名

  • (4):非静态内部类中不能声明任何 static 成员

  • (5):内部类可以互相调用,如下:

class A {

    class B  {  }   //B、C 间可以互相调用

    class C  {  }

}

4.2:成员式内部类的访问

  • (1):内部类可以直接访问外部类属性—-内部类的对象以属性的方式记录其所依赖的外层类对象的引用,因而可以找到该外层类对象并访问其成员。该属性是系统自动为非 static 的内部类添加的,名称约定为“外层类名.this”。

  • (2):在另一个外部类中使用非静态内部类中定义的方法时,要先创建外部类的对象,再创建与外部类相关的内部类的对象,再调用内部类的方法,这样要求是因为:外部类的static方法中不存在当前对象,或者其它无关类中方法的当前对象类型不符合要求。如下所示:

class Outer2{

    private int size;

    class Inner{

        public void dostuff(){ size++; }

    }

}

class TestInner{

    public static void main(String[] args){

        Outer2  outer = new Outer2();           //其它外部类先创建外部类

        Outer2.Inner  inner=outer.new Inner();  //外部类再创建内部类

        inner.dostuff();

    }

}
  • (3):static 内部类相当于其外部类的 static 成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。

示例如下:

class Outer2 {

    private static int size;

    static class Inner {        //静态内部类

        public void dostuff() {

            size++;

            System.out.println("size="+size);

        }

    }

}

public class Test {

    public static void main(String[] args) { 
        Outer2.Inner inner = new Outer2.Inner(); //静态内部类可以直接点取获取
        inner.dostuff();
    }

}

运行结果为:
size=1
  • (4):由于内部类可以直接访问其外部类的成分,因此当内部类与其外部类中存在同名属性或方法时,也将导致命名冲突。所以在多层调用时要指明,如下所示:
public class Outer3 {
    private int size;

    public class Inner {
        private int size;

        public void dostuff(int size) {

            size++; // 本地的 size;
            this.size++; // 内部类的 size

            Outer3.this.size++; // 外部类的 size
        }
    }
}

6.5:本地内部类

本地类(Local class)是定义在代码块中的类.它们只在定义它们的代码块中是可见的.

本地类有几个重要特性:

  • (1):仅在定义了它们的代码块中是可见的;

  • (2):可以使用定义它们的代码块中的任何本地 final 变量;

  • (3):本地类不可以是 static 的,里边也不能定义 static 成员。

  • (4):本地类不可以用 public、private、protected 修饰,只能使用缺省的。

  • (5):本地类可以是 abstract 的。

示例如下:

public final class Outter{

    public static final int TOTAL_NUMBER= 5 ;

    public int id=123;

    public void t1(){

        final int a = 15;

        String s = "t1";

        class Inner{

            public void innerTest(){

                System.out.println(TOTAL_NUMBER);   //可以访问外部类的静态final
                System.out.println(id);
                System.out.println(a);
                //System.out.println(s);不合法,只能访问本地方法的final变量
            }

        }

        new Inner().innerTest();

    }

    public static void main(String[] args) {

        Outter t = new Outter ();
        t.t1();

    }

}

6.6:匿名内部类

6.6.1 匿名内部类概述

匿名内部类是本地内部类的一种特殊形式,也就是没有类名的内部类,而且具体的类实现会写在这个内部类里面。

把上面的例子改造一下,如下所示:

public final class Test{

    public static final int TOTAL_NUMBER= 5 ;

    public int id=123;

    public void t1(){

        final int a = 15;
        String s = "t1";

        new Aclass(){
            public void testA(){
                System.out.println(TOTAL_NUMBER);
                System.out.println(id);
                System.out.println(a);
                //System.out.println(s);不合法,只能访问本地方法的final变量
            }

        }.testA();

    }

    public static void main(String[] args) {
        Test t = new Test();
        t.t1();
    }

}


class   Aclass{
    public void testA(){}
}

注意:匿名内部类是在一个语句里面,所以后面需要加“;”。

6.6.2:匿名类的规则

  • (1):匿名类没有构造方法;

  • (2):匿名类不能定义静态的成员;

  • (3):匿名类不能用 4 种权限、static、final、abstract 修饰;

  • (4):只可以创建一个匿名类实例

再次示例:

public class Outter{

    public Contents getCont() {

        return new Contents() {

            private int i = 11;

            public int value() { return i; }

        };

    }

    public static void main(String[] args) {
        Outter  p = new Outter ();
        Contents c = p. getCont ();
    }

}

6.7:内部类规则小结

  • (1):类名称只能用在定义过的范围中,除非用在限定的名称中。内部类的名称必须与所嵌套的类不同。

  • (2):内部类可以被定义在方法中。这条规则较简单,它支配到所嵌套类方法的变量的访问。任何变量,不论是本地变量还是正式参数,如果变量被标记为 final,那么,就可以被内部类中的方法访问。

  • (3):内部类可以使用所嵌套类的类变量和实例变量以及所嵌套的块中的本地变量。

  • (4):内部类可以被定义为 abstract。

  • (5):只有内部类可以被声明为 private 或 protected,以便防护它们不受来自外部类的访问。访问保护不阻止内部类使用其它类的任何成员,只要一个类嵌套另一个。

  • (6):一个内部类可以作为一个接口,由另一个内部类实现。

  • (7):被自动地声明为 static 的内部类成为顶层类。这些内部类失去了在本地范围和其它内部类中使用数据或变量的能力。

  • (8):内部类不能声明任何 static 成员;只有顶层类可以声明 static 成员。因此,一个需求 static 成员的内部类必须使用来自顶层类的成员。

注:文中内容来自网络文字总结.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值