Java面向对象之接口、多态、内部类
子父类中变量的特点:
如果子父类中出现非私有的同名成员变量时,子类要访问本类中的变量用this,子类要访问父类中的同名变量用super。 super的使用和this的使用几乎一致。this代表的是本类对象的引用,super代表的是父类对象的引用。请看下面代码:
class Fu
{
int num = 8;
}
class Zi extends Fu
{
int num = 5;
void show()
{
//子类要访问父类中的同名变量用super。
System.out.println("num="+super.num);
}
}
class Extends
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
}
}
子父类中函数的特点:
当子类出现和父类一样的函数,如果子类对象调用该函数,那么会运行子类函数的内容,如同父类的函数被覆盖一样。这种情况是函数的另一个特性—重写(覆盖)。当子类继承父类,沿袭了父类的功能到子类中,子类虽然具备该功能,但是功能的内容却和父类不一致。这时就没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义并重写功能内容
覆盖注意:1.子类覆盖父类必须保证子类权限大于或等于父类权限才可以覆盖,否则编译失败。 2.静态只能覆盖静态。
重载只看同名函数的参数列表,重写是子父类方法要一模一样。
覆盖代码示例:
class Fu
{
void show()
{
System.out.println("a");
}
void speak()
{
System.out.println("5");
}
}
class Zi extends Fu
{
void sperk()
{
System.out.println("6");
}
void show()
{
System.out.println("b");
}
}
class Extends1
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
z.speak();
}
}
子父类中构造函数的特点:
在对子类对象进行初始化时父类的构造函数也会运行,因为在子类的构造函数里面的第一行有一个默认的super()语句。 super()会访问父类中空参数的构造函数,并且子类中所有的构造函数里面第一行都有一个super() 。
为什么子类一定要访问父类中的构造函数呢?因为父类中的数据子类可以直接获取。所以子类对象在建立时需要先查看父类是如何对这些数据进行初始化的。因此,子类在对象初始化时要先访问一下父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。如果父类中没有空参数的构造函数,那么在子类的构造函数中必须指定要访问的父类中的构造函数。
请看下面代码:
class Fu
{
int num ;
Fu()
{
//super(); 子类每一个构造函数内的第一行都有一句隐式super()
num= 60;
System.out.println("fu run");
}
Fu(int x)
{
System.out.println("fu ...."+x);
}
}
class Zi extends Fu
{
Zi()
{
super();
//super(4);
System.out.println("zi run");
}
Zi(int x)
{
this();
//super(); 子类每一个构造函数内的第一行都有一句隐式super()
//super(3);
System.out.println("zi..."+x);
}
}
class ExtendsDemo4
{
public static void main(String[] args)
{
Zi z = new Zi(0);
System.out.println(z.num);
}
}
结论:子类的实例化过程子类中的所有构造函数都会默认访问父类中空参数的构造函数,子类中每一个构造函数里面的第一行都有一个隐藏式的super()。子类的构造函数里面的第一行也可以通过手动指定this语句来访问本类中的构造函数,子类中至少会有一个构造函数会访问父类中的构造函数。
注意:当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。super()一定要定义在子类构造函数的第一行。
final
关键字,有最终的意思。是一个修饰符。final可以修饰类、函数、变量。
1.被final修饰的类不可以被继承,为了避免被子类继承复写其功能。
2.被final修饰的方法不可以被复写。
3.被final修饰的变量会成为常量只能赋值一次,final既可以修饰成员变量也可以修饰局部变量。
final代码示例:
class Demo
{
final int x = 3;
public static final double PI = 3.14;
final void show1()
{}
void show2()
{
final int y = 4;
System.out.println(3.14);
}
}
class SubDemo extends Demo
{
void show1(){}
}
当在描述事物时,一些数据的出现值是固定的,为了增强阅读性都给这些值起个名字,方便阅读。而这个值不需要改变,所以加上final修饰成为常量。常量名字的书写规范是所有字母都大写,如果由多个单词组成单词间通过_连接。
内部类定义在类中的局部位置上时只能访问该局部被final修饰的局部变量。
abstract
关键字,抽象:就是看不懂的。 abstract void show();这是抽象方法。
当多个类中出现相同功能而功能主体却不同时,可以进行向上抽取,只抽取功能定义,不抽取功能主体。抽象类的特点:
1.抽象方法一定在抽象类中。
2.抽象方法和抽象类都必须被abstract关键字修饰。
3.抽象类不可以用new创建对象,因为调用抽象方法没意义。
4.抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
抽象类和一般类没有太大不同,该如何描述事物就如何描述事物,只不过该事物中出现了一些看不懂的东西,这些不确定的部分也是该事物的功能,需要明确出来,但是无法定义主体。通过抽象方法来表示。1.抽象类比一般类多了抽象方法,就是在类中可以定义抽象方法。2.抽象类不可以实例化。
抽象类中可以不定义抽象方法,这样做是为了不让该类建立对象。
模版方法设计模式:
在定义功能时,功能的一部分是确定的,而另外一部分是不确定的,并且确定的部分在使用不确定的部分,也就是说不确定的部分在确定的部分中间,那么这时就将不确定的部分暴露出去,由该类的子类去完成。
需求:获取一段程序运行的时间。原理:获取程序开始和结束的时间并相减即可。使用模版方法设计模式如下:
abstract class GetTime
{
public final void getTime()
{
long start = System.currentTimeMillis(); //开始时间。
runCode(); //运行中的程序。
long end = System.currentTimeMillis(); //结束时间。
System.out.println("毫秒="+(end-start));
}
public abstract void runCode();
}
class SubTime extends GetTime
{
public void runCode()
{
for(int x=0; x<1000; x++)
{
System.out.print(x);
}
}
}
class TemplateDemo
{
public static void main(String[] args)
{
SubTime sb = new SubTime();
sb.getTime();
}
}
接口:
格式:interface {} implements 关键字,实现的意思,用于类与接口之间的连接。
接口中常见定义:常量、抽象方法。
接口中的成员修饰符是固定的:1.成员常量:public static final 2.成员函数:public abstract
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。 接口中的成员都是public的 。
class 用于定义类。interface 用于定义接口。
接口是不可以创建对象的,因为有抽象方法。需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例
化。否则子类是一个抽象类。
接口可以被类多实现,也就是说一个类可以同时实现多个接口。 接口可以用于定义功能扩展,由子类去实现。
基本功能定义在类中,扩展(特有)功能定义在接口中。
interface Inter
{
public static final int NUM = 3;
public abstract void show();
}
interface InterA
{
public abstract void show();
}
class Demo
{
public void function(){}
}
class Test extends Demo implements Inter,InterA
{
public void show(){}
}
多态:
可以理解为事物存在的多种体现形态。
1.多态的体现:
父类的引用指向了自己的子类对象(可以接收自己的子类对象)。 Animal x = new Cat()父类的引用指向了自己的子类对象
2.多态的前提:
必须是类与类之间有关系,要么继承,要么实现。还有一个前提就是存在覆盖。
3.多态的好处:
多态的出现大大的提高了程序的扩展性。多态的弊端:虽然提高了扩展性,但是只能使用父类的引用访问父类中的成员。
当父类的引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转型。
类型提升也称为向上转型:子类型转成父类型。
向下转型:强制将父类的引用转成子类类型。
注意:不能将父类对象转成子类类型。多态自始至终都是子类对象在发生着变化。
abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void kanJia()
{
System.out.println("看家");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("饲料");
}
public void gongDi()
{
System.out.println("拱地");
}
}
instanceof 关键字,用于引用数据类型之间的判断。
在多态中,父类的引用访问子类中的成员,如果父类中没有该成员则编译失败。
在多态中成员函数的特点:
在编译时期:参阅引用型变量所属的类(父类)中是否有调用的方法,如果有则编译通过,如果没有则编译失败。
在运行时期:参阅对象所属的类(子类)中是否有调用的方法。
简单总结就是:在多态中调用成员函数时,编译看左边,运行看右边。 Fu f = new Zi();
在多态中成员变量的特点:(面试中容易见到,开发中很难看到) 无论编译或是运行都参考左边(引用型变量所属的类)。
在多态中静态成员的特点:(面试中容易见到,开发中很难看到) 无论编译或是运行都参考左边(引用型变量所属的类)。
内部类:
将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类、嵌套类)。
访问特点:内部类可以直接访问外部类中的成员,包括私有成员,因为内部类中持有了一个外部类的引用,格式:外部类
名.this 。而外部类要想访问内部类中的成员必须要建立内部类的对象。
如何创建内部类的对象呢?
当内部类定义在外部类的成员位置上且非私有,可以在外部其他类中直接建立内部类对象。
外部类名.内部类名 变量名 = new 外部类名().new 内部类名(); Outer.Inner in = new Outer().new Inner();
当内部类在成员位置上,就可以被成员修饰符修饰。私有和静态是修饰成员的。
比如:private:将内部类在外部类中进行封装。
内部类演示代码:
class Outer
{
private int x=5;
class Inner //内部类
{
int x = 6;
public void function()
{
int x = 7;
System.out.println("Inner::"+Outer.this.x);
}
}
public void show()
{
Inner in = new Inner();
in.function();
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer out = new Outer();
out.show();
Outer.Inner in = new Outer().new Inner();
in.function();
}
}
static:内部类就具备静态的特性。当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。
在外部其他类中如何直接访问static内部类的非静态成员呢? new Outer.Inner().function();
在外部其他类中如何直接访问static内部类的静态成员呢? Outer.Inner.function();
注意:
当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法访问内部类时,内部类也必须是静态的。
内部类定义的原则:
当描述事物时,事物的内部还有事物,那么该事物用内部类来描述。因为内部事物在使用外部事物中的内容。
当一个类需要直接访问另外一个类中的成员的时候,那么就把这个类写到那个类的里面成为内部类。也可以进行封装不对
外暴露,而是对外提供访问的方法。
内部类也可以定义在局部中,当定义在局部时:
1.不可以被成员修饰符(private、static)修饰。
2.可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
匿名内部类是内部类的简写格式。 定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
匿名内部类的格式: new 父类或者接口(){定义子类的内容}
匿名内部类就是一个匿名子类对象,而且这个对象有点胖。可以理解为带内容的对象。匿名内部类中定义的方法最好不要
超过3个。
匿名内部类代码示例:
abstract class AbsDemo
{
abstract void function();
abstract void ok();
}
class Outer
{
int x =90;
/*
class Inner
{
void amen()
{
System.out.println("amen="+x);
}
}
*/
void show()
{
//new Inner().amen();
AbsDemo ad = new AbsDemo()
{
void function()
{
System.out.println("amen="+x);
}
void ok()
{
System.out.println("ok="+x);
}
void yesu()
{
System.out.println("yesu="+x);
}
};
ad.function();
ad.ok();
}
}
class InnerClassDemo1
{
public static void main(String[] args)
{
new Outer().show();
}
}