• 黑马程序员——JAVA学习笔记四(继承、接口、内部类)


    1,    通过extends关键字让类与类之间产生继承关系。多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需定义这些属性和行为,只要继承那个类即可,已存在的类叫做超类,基类,或父类。新类称为子类,派生类,孩子类。
    子类可以直接访问父类中的非私有的属性和行为。子类无法继承父类中私有的内容。JAVA不支持多继承,只支持单继承,多实现。
    继承提高了代码复用性,让类与类之间产生了关系。为多态提供了前提。
     
    2,    super关键字代表父类中成员变量内存空间的标示。两个作用:调用超类的方法或变量,2,调用超类的构造器。
     
    3,    覆盖,当子父类中出现的成员函数一模一样的情况,会运行子类的函数。这种现象叫做,覆盖操作。当子类需要父类的功能,而功能主体子类有自己的特有内容时,可以复写父类的方法。注意覆盖和重载的区别。父类私有方法和static方法不可以覆盖,只是隐藏了。覆盖时,子类的方法权限一定要大于等于父类权限。允许子类将覆盖方法的返回类型定义为原返回类型的子类型。
     
    3,       构造函数,子类中构造函数默认调用子类默认构造函数。先初始化子类,然后才是父类。父类构造函数必须放在第一行。对象初始化顺序,如下:
     
    一个对象实例化过程,以Person p = new Person();为例:
    1、JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下)。
    2、在内存中开辟空间,并分配地址。
    3、并在对象空间中,对对象的属性进行默认初始化。
    4、调用对应的构造函数进行初始化。
    5、在构造函数中,第一行会先到调用父类中构造函数进行初始化。
    6、父类初始化完毕后,再对子类的属性进行显示初始化。
    7、再进行子类构造函数的特定初始化。
    8、初始化完毕后,将地址值赋值给引用变量。
     
    4,    final关键字:
    final可以修饰类,方法,变量。
    final修饰的类不可以被继承。
    final修饰的方法不可以被覆盖。
    final修饰的变量是一个常量,只能被赋值一次。
     
    为什么要用final修饰变量,其实在程序中如果一个数据是固定的。
    那么直接使用这个数据就可以了,但是这种阅读性差,所以应该给数据起个名称。
    而且这个变量名称的值不能变化,所以加上final固定。
    写法规范:常量所有字母都大写,多个单词,中间用_连接。
     
    5,    抽象类,抽象就是从多个事物中将共性的内容提取出来。JAVA可以定义没有方法的体的方法,该方法的具体实现有子类完成,该方法称为抽象方法,该类称为抽象类。抽象类和抽象方法必须用abstract关键字来修饰。
    抽象类是从具体事物抽取而来,本身不是具体的,没有对应的实例,不能被实例化。 抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
    抽象类中是否有构造函数?
    答:有,用于给子类对象进行初始化。
     
    抽象关键字abstract不可以和哪些关键字共存?
    答:private、static、final。
    final:不可以在子类定义覆盖其方法。
     
    抽象类中可不可以没有抽象方法?
    答:可以,但是很少见。目的就是不让该类创建对象,AWT的适配器对象就是这种类。通常这个类中的方法有方法体,但是却没有内容。
    相同点:
    抽象类和一般类都是用来描述事物的,都在内部定义了成员。
    不同点:
    ①一般类有足够的信息描述事物。
       抽象类描述事物的信息有可能不足。
    ②一般类中不能定义抽象方法,只能定义非抽象方法。
       抽象类中可定义抽象方法,同时也可以定义非抽象方法。
    ③一般类可以被实例化。
       抽象类不可以被实例化。
     
    6,    接口,
    当一个抽象类中的方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口。
    ①虽然抽象类中的全局变量和抽象方法的修饰符都可以不用写,但是这样阅读性很差。所以,最好写上。
    ②类与类之间是继承关系,类与接口直接是实现关系。
    ③接口不可以实例化,只能由实现了接口的子类并覆盖了接口中所有的抽象方法后,该子类才可以实例化。否则,这个子类就是一个抽象类。
    抽象类和接口的异同点?
    相同点:
    都是不断向上抽取而来的。
    不同点:
    ①抽象类需要被继承,而且只能单继承。
       接口需要被实现,而且可以多实现。
    ②抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。
       接口中只能定义抽象方法,必须由子类去实现。
    ③抽象类的继承,是is a关系,定义该体系的基本共性内容。
       接口的实现是like a关系。
     
    7,    多态,一个对象变量可以指示多种实际类型的的现象称为多态。是"IS -A“规则。
    体现:
    父类或者接口的引用指向或者接收自己的子类对象。
    作用:
    多态的存在提高了程序的扩展性和后期可维护性。
    前提:
    ①需要存在继承或者实现关系。
    ②需要有覆盖操作。
    好处:
    提高了代码的扩展性,前期定义的代码可以使用后期的内容。
    弊端:
            前期定义的内容不能使用(调用)后期子类的特有内容。
    JAVA中,子类数组引用可以转化为超类数组引用,不需要强制类型转换,但是需要注意,时时刻刻记住创建数组的类型。
    instanceof :用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断,NULL不会报错。
     
    成员变量
         编译时:参考引用型变量所属的类中的是否有调用的成员变量。有,编译通过,没有,编译失败。
         运行时:参考引用型变量所属的类中的是否有调用的成员变量,并运行该所属类中的成员变量。
         简单说:编译和运行都参考等号的左边。
    成员函数(非静态)
         编译时:参考引用型变量所属的类中是否有调用的函数。有,编译通过。没有,编译失败。
         运行时:参考的是对象所属的类中是否有调用的函数。
         简单说:编译看左边,运行看右边。
    静态函数
         编译时:参考的是对象所属的类中是否有调用的函数。
         运行时:参考的是对象所属的类中是否有调用的函数。
         简单说:编译和运行看左边。

     
    8,    动态绑定,对象方法调用时步骤,候选方法->重载解析->调用。
    如果是private方法,static方法,final方法或者构造器,那么编译器将可以准确的知道应该调用那个方法,这种调用方式叫做静态绑定。与此对应的是,调用的方法依赖于隐式的参数类型,并且在运行时实现动态绑定。编译器采用动态绑定的方式生成一条调用其方法的指令。当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与X所引用对象的实际类型最合适的那个方法。
     
            每次调用方法都要进行搜索,时间开销相当大,因此,虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用的方法。
     
    9,Object类,所有类的超类,  用==比较基本类型, equals比较对象域,比较两个对象是否具有相同的引用。
    equals()方法:自反性(x!=null, x.equals(x)=ture),对称性( x.equals(y)= y.equals(x) ),传递性(x=y, y= z, 则 x=z),一致性(如果引用对象没有发生变化,则结果也不会变),如果子类能拥有自己的相等概念。则对称性需求将强制采用getClass进行检测。如果超类决定相等的概念,那么就可以用instanceof进行检测,这样可以用于不同子类进行相等检测。@override防止没有覆盖父类方法。
     
    hashcode()方法,有对象导出的一个整形值。默认是对象的存储存地址。String中是有内容组成的。如果重新定义equals方法,则也需要重新定义hashCode方法。他们的定义必须一致。
     
    clone()方法,深拷贝则需要用clone()方法,他是protect方法,用户不能直接调用它。一般情况下:如果要深拷贝,必须要重新定义clone方法。
    对于每一个类,需要作以下判断:
    1, 默认的clone方法是否满足需求。
    2,默认的clone方法是否能够通过调用可变子对象的clone得到修补。
    3, 是否不应该使用clone。
    要选择1或者2,则:
    实现Cloneable接口。(该接口仅起一个标记作用,如果一个对象需要克隆,而没有实现cloneable接口,则会抛出一个异常。现在可以返回object类的子类,斜边返回类型)
    使用public访问修饰符重新定义clone方法。
     
    10,    对象包装器,Integer ,Long, Double, Float, Short, Byte,Chararter,Void, Boolean(前6个派生于Number类)。装箱:XXX.valueOf() 拆箱:XXX.xxvalue(),自动装箱规范要求boolean,byte,char<= 127. 介于-128~127之间的short 和int被包装到固定的对象中。
     
    11,    可变参数,当成数组使用。
    枚举类:可以直接用==比较,不需要equals比较。如果需要,可以在枚举类中添加一些构造器、方法和域。当然,构造器只是在构造枚举常量是被调用。所有的枚举类型都是Enum类的子类,常用方法toSTring()。valueOf(class, string), values().
     
    12,    接口与回调,回调是一种常见的程序设计模式,在这种模式中,可以指出某个特定事件。由于对象可以携带一些附加的信息,所以传递一个对象比传递一个函数要灵活得多。
     
    13,    内部类,将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)。 
    为什么要使用内部类?
    内部类可以直接访问外部类中的成员,包括私有成员。
    内部类可以对同一个包中的其他类隐藏。
    当要定义一个回调函数不想写大量的代码,使用匿名内部类比较便捷。
    对外部类的引用:outer.this   
    在外部类作用域之外,可以这样使用内部类:outerclass.innerclass
    编译器会把内部类翻译成用$分割外部类名和内部类名的常规类文件。
    编译器会生成this$0的变量外部对象引用。所有的名字都会加上outer$。
    内部类访问外部类私有域,会在外部类生成一个静态方法。。这样有安全隐患。同时还会生成构造器。
    内部类定义在成员位置上,可以被private、static成员修饰符修饰。被static修饰的内部类只能访问外部类中的静态成员。
    如果内部类中定义了静态成员,该内部类也必须是静态的!
     
    内部类定义在局部位置上,也可以直接访问外部类中的成员。局部类修饰符不能用public private 访问说明符号进行申明,可以对外部世界完全隐藏起来,即使外部类方法也不能访问。
     
    匿名内部类:
    定义:
    就是内部类的简化写法。
    前提:
    内部类可以继承或实现一个外部类或者接口。
    格式:
    new 外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容。)}
    简单理解:
    就是建立一个带内容的外部类或者接口的子类匿名对象。
    什么时候使用匿名内部类呢?
    通常在使用方法是接口类型参数,并且该接口中的方法不超过三个时,可以将匿名内部类作为参数传递。
    由于构造器的名字必须和类名一致,而匿名类没有类名,所以,匿名类不能有构造器,取而代之将构造器传递给超类构造器。
    声明在接口中的内部类默认自动为public和Static.
     
    技巧:双括号初始化。
    ArrayList<String> friends = new ArrayList();
    friends.add("nihao");
    friends.add("hell:");
    invite(friends);
    可简化为:invite(new ArrayList<String>(){{ add("Hary"); add("Tony"); }});    构造代码块。
     
  • 相关阅读:
    [树形dp] Luogu P4516 潜入行动
    [kruskal][Trie] Codeforces 888G Xor-MST
    [线性基] Luogu P4151 最大XOR和路径
    [线段树] Luogu P4560 砖墙
    [递归][重心] Luogu P4886 快递员
    [Trie][贪心][堆] LibreOJ #3048 异或粽子
    [长链剖分][优先队列] LibreOJ #3052 春节十二响
    [支配树] Bzoj P2815 灾难
    [长链剖分][线段树] Bzoj P1758 重建计划
    [dsu on tree] Codeforces 600E Lomsat gelral
  • 原文地址:https://www.cnblogs.com/solaire/p/4178797.html
Copyright © 2020-2023  润新知