java基础知识--继承与多态
6.01 继承的定义
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
(1)语法格式:
[类修饰符] class 子类名 extends 父类名{
语句;
}
(2) 优缺点
继承的好处
提高了代码的复用性
提高了代码的维护性
让类与类之间产生了关系,是多态的前提
缺点
增加了耦合性
OOP思想开发原则:高内聚,低耦合
耦合:类与类之间的关系
内聚:自身完成事情的能力
注意
1、子类拥有父类得特征,而父类没有,父类更通用,子类更具体,(特征包括属性和方法,自身的特性,拥有父类没有的)。
2.继承是面向对象思想的三大特性之一,使类与类之间产生特殊 --一般的关系,即is-a关系。
3、父类中一般只定义一般属性和方法(这个一般可以理解为是子类共有的,这就是父类更通用,而子类拥有其他的,所以子类更具体)。
4、成员变量和成员方法可以被继承,但是构造方法不能被继承。子类中通过super关键字来调用父构造方法。
5、在子类中可以继承父类得那些东西,父类中public,protected修饰的属性,方法可以继承。private修饰的属性和方法不能被继承。
6、规则: 创建子类对象的时候,首先调用的是父类的无参构造方法创建一个父类对象。可以在子类中显示调用父类的有参构造方法。
7、子类继承父类,子类拥有父类的属性和方法,并且子类可以拓展具有父类所没有的一些属性和方法。
8、如果父类的属性均为private修饰,则可以通过getter,setter方法来调用。
9.在java中,java.lang.Object 类是所有java类的最高层父类, 如果在类的声明中未使用extends 关键字指明其父类,则默认父类为Object 类。
(3)继承中 成员变量的特点
继承中成员变量和父类变量重名的情况下:
局部变量->成员变量->父类的成员变量
继承中成员变量和父类变量不重名的情况下:
局部变量->成员变量->子类静态变量->父类静态变量
成员变量和静态变量不可以重名
总而言之:(调用顺序)
局部变量->成员变量->子类静态变量->父类成员变量->父类静态变量
(4)构造函数在继承中的特点
1.创建子类对象的时候 父类的构造函数运行了。
class Fu{
Fu(int a){
System.out.println("Fu constructor...");
}
Fu(int a,int b){
}
}
class Zi extends Fu{
Zi(){
super(10); //调用父类构造函数
System.out.println("Zi constructor...");
}
}
为何子类在创建对象时 父类的构造函数要执行呢?
不是为了创建父类对象
而是为了给子类准备准备(将被继承的属性进行初始化!)
2.如果子类的构造函数中 没有单向调用 则每个构造函数的第一句都会去调用父类的构造函数super(...)。
3.如果子类的构造函数中 有单向调用 则最后一个被调用的构造函数第一句绝对是super(...)。
4.如果父类的构造函数为私有 子类无法创建对象。
5.如果父类的默认无参构造函数没有了(显式出来) 子类默认可能无法访问super()。
注意:
父类的构造函数运行不代表父类对象的创建。
(5)成员函数在继承中的特点
如果函数不重名
对象.函数名调用;
如果函数重名
称之为函数的重写,重写:同类中函数名相同,参数列表不同,即函数定义全部一样(返回值类型 函数名 参数列表 除了权限)
权限:子类重写的函数权限要大于等于父类
public>protected>默认>private
父类的函数为private 子类相同函数不是重写,是子类特有的函数。
static所修饰的函数,就算符合重写的定义,但不是重写,属于子类特有的函数。
5.02 super
super可以理解为是指向自己父类对象的一个指针,而这个超类指的是离自己最近的一个父类。
super用于限定该对象调用它从父类继承得到的实例变量或方法。
a)super和this相同,都不能出现在静态方法中,因为静态方法属于类的,调用静态方法的可能是个类,而不是对象,而super和this都是限定对象调用。
b)super同样也可以在子类中调用父类中被子类隐藏和覆盖的同名实例变量和同名方法。
c)在构造函数中使用super,则super会用于限定于该构造函数初始化的是该对象从父类继承得到的实例变量,而不是该类自己定义的实例变量。意思就是调用父类的构造函数。
d) super限定:super紧跟一个点
super.name; super.walk();
e)super调用:super紧跟圆括号
super(参数)
f)super不代表父类的对象的引用 仅仅表明一个父类空间
父类空间中只有父类给子类留下的非私有属性
super在堆内存中对象的所属空间里
g) 父类私有的不能被继承!super也不可访问。
三种用法:
1.普通的直接引用
与this类似,super相当于是指向当前对象的父类,这样就可以用super.xxx来引用父类的成员。
2.子类中的成员变量或方法与父类中的成员变量或方法同名
class Country {
String name;
void value() {
name = "China";
}
}
class City extends Country {
String name;
void value() {
name = "Shanghai";
super.value(); //调用父类的方法
System.out.println(name);
System.out.println(super.name);
}
3.引用构造函数
super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
class Person {
public static void prt(String s) {
System.out.println(s);
}
Person() {
prt("父类·无参数构造方法: "+"A Person.");
}//构造方法(1)
public class Chinese extends Person {
Chinese() {
super(); // 调用父类构造方法
System.out.println("子类·调用父类”无参数构造方法“: "+"A chinese coder.");
}
6.02 单继承与多继承
单继承也就是一个子类只有一个父类
多继承就是一个子类可以有多个父类
继承可以使用 extends 和 implements 这两个关键字来实现继承。
单继承(extends关键字)
在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
多继承(implements关键字)
使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
区别
二者区别,一个是继承(可以有构造函数),一个是接口的实现(并没有什么构造函数),只是接口,可以被其他类继承而已。
6.05 关于继承的例题
class Test1{
public static void main(String[] args){
//*Error
A a=new A();
System.out.println(a==a.a); //报错 栈溢出
B b=new B();
System.out.println(b==b.b);//false
System.out.println(b.b==b.b.b);//true
//0x333 0x333.b
}
}
class B{
static B b=new B();
}
class A{
A a=new A();
}
2.栈
class StackDemo{
public static void main(String[] args){
}
}
abstract class MyStack{ //父类的栈
public void push(); //进栈一个元素
public int pop(); //弹栈一个元素
public int peek(); //获取当前栈顶该元素
public int size(); //获取有效长度
public void print(); //打印
public void clear(); //清空
}
class ArrayStack extends MyStack{ //子类的线性栈
private int size;//有效长度
private int[] array;//元素容器
public ArrayStack(){
this(10);
}
public ArrayStack(int capacity){
this.array=new int[capacity];
this.size=0;
}
public void push(int e){
if(size==array.length){
resize(array.length*2);
}
array[size]=e;
size++;
}
public int pop(){
if(size==0){
System.out.println("栈为空!");
return -1;
}
int e=array[size-1];
size--;
if(size==array.length/4&&array.length>10){
resize(array.length/2);
}
return e;
}
public int peek(){
return array[size-1];
}
public int size(){
return size;
}
public void clear(){
size=0;
array=new int[10];
}
private void resize(int capacity){
int[] newArray=new int[capacity];
for(int i=0;i<Math.min(array.length,newArray.length);i++){
newArray[i]=array[i];
}
array=newArray;
}
private int getCapacity(){
return array.length;
}
public void print(){
if(size==0){
System.out.println("bottom[]top "+size()+"/"+getCapacity());
}else{
String s="bottom[";
for(int i=0;i<size;i++){
if(i==size-1){
s+=array[i]+"]top "+size()+"/"+getCapacity();
}else{
s+=array[i]+",";
}
}
System.out.println(s);
}
}
}
class ChainStack extends Stack{ //子类 链栈
private int size;
private Node head;
ChainStack(){
size=0;
this.head=new Node();//创建结点
}
ChainStack(int[] arr){
this();
for(int i=0;i<arr.length;i++) {
push(arr[i]);
}
}
private class Node{
int e;
Node next=null;
Node(){
this(0,null);
}
Node(int e,Node next){
this.e=e;
this.next=next;
}
}
public void push(int e) { //尾插法
Node n=new Node(e,null);
n.next=head.next;
head.next=n;
//head.next=new Node(e,head.next); 和上面两句一个意思
}
public void push() {
}
public void print() {
String s="bottom[";
Node p=head;
while(true) {
if(p.next!=null) {
s+=p.e+",";
p=p.next;
}else {
s+=p.e+"]top";
break;
}
}
System.out.println(s);
}
}
6.05 多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,多态性是对象多种表现形式的体现。
在代码中表示为 子类对象指向父类引用 父类 变量名=new 子类();
例:
现实中,比如我们按下 F1 键这个动作:
如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
如果当前在 Word 下弹出的就是 Word 帮助;
在 Windows 下弹出的就是 Windows 帮助和支持。
同一个事件发生在不同的对象上会产生不同的结果。
(1) 多态的优点
1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性
(2)多态存在的三个必要条件
继承
重写
父类引用指向子类对象
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型 ( 父类引用指向子类对象)
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal { //抽象父类
abstract void eat();
}
class Cat extends Animal { //子类Cat
public void eat() { //重写eat()
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal { //子类Dog
public void eat() { //重写eat()
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
(3)成员变量 在多态中的特点
只能访问父类中的成员变量
(4)成员函数 在多态中的特点
如果有重写 则调用重写
如果没有重写 则调用父类成员函数
如果没有重写 父类也没有 则报错
总之 子类的特有不可调用
(5)静态变量 在多态中的特点
只能访问父类中的静态变量
(6)静态函数 在多态中的特点
只能访问父类中的静态函数(间接证明了 静态函数没有重写)
6.06 单例模式
该类只能创建一个对象
单线程环境下的单例模式
1.外界不能直接创建对象--构造函数私有化
2.对象在内部创建 new Single();
3.该对象最终要提供出去,getter();
4.get不能为成员函数!所以是static
5.必须保证类的内部,new只执行一次 static
饿汉式
直接返回创建的对象
class Single{
private static Single s=new Single();
private Single(){
}
public static Single getInstance(){
return s;
}
}
饱汉式
先判断对象是否为空 再返回对象
class Single2{
private static Single2 s=null;
private Single2(){
}
public static Single2 getInstance(){
if(s==null){
s=new Single2();
}
return s;
}
}
小结
1.可以从现有的类定义新的类,这称为类的继承。新类称为次类、子类或继承类;现有的类称为超类 、父类或基类。
2.构造方法用来构造类的实例。不同于属性和方法,子类不继承父类的构造方法。它们只能用关键字super 从子类的构造方法中调用。
3.构造方法可以调用重载的构造方法或它的父类的构造方法。这种调用必须是构造方法的第一条语句。如果没有显式地调用它们中的任何一个,编译器就会把 super()作为构造方法的第一条语句,它调用的是父类的无参构造方法。
4.为了重写一个方法,必须使用与它的父类中的方法相同的签名来定义子类中的方法。
5.实例方法只有在可访问时才能重写。这样,私有方法是不能重写的,因为它是不能在类本身之外访问的。如果子类中定义的方法在父类中是私有的,那么这两个方法是完全没有关系的。
6.静态方法与实例方法一样可以继承。但是,静态方法不能重写,如果父类中定义的静态方法在子类中重新定义,那么父类中定义的方法被隐藏。
7.Java 中的每个类都继承自 java.Ung.Object 类。如果一个类在定义时没有指定继承关系,那么它的父类就是 Object。
8.如果一个方法的参数类型是父类(例如:Object), 可以向该方法的参数传递任何子类(例如:Circle 类或 String 类)的对象。这称为多态。
9.因为子类的实例总是它的父类的实例,所以,总是可以将一个子类的实例转换成一个父类的变量。当把父类实例转换成它的子类变量时,必须使用转换记号(子类名)进行显式强制转换,向编译器表明你的意图。
10.— 个类定义一个类型。子类定义的类型称为子类型,而父类定义的类型称为父类型。
11 可以使用表达式 obj instanceof AClass (对象名 instanceof 类名)测试一个对象是否是一个类的实例。
12.可以使用 ArrayList 类来创建一个对象,用于存储一个对象列表。
13.可以使用 protected 修饰符来防止方法和数据被不同包的非子类访问。
14.可以用 final 修饰符来表明一个类是最终类,是不能被继承的;也可以表明一个方法是最终的,是不能重写的。
上一篇: 找出数组中每个数的右边第一个比它大的数
下一篇: 判断一个数是否为素数