面向对象的另外两个特征:继承和多态
继承:
1,提高了代码的复用性。
2,让类与类之间产生了关系。有了这个关系,才有了多态的特性。
注意:千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。
子父类出现后,类成员的特点:
类中成员:
1,变量。
2,函数。
3,构造函数。
1,变量
如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this,子类要访问父类中的同名变量,用super。
super的使用和this的使用几乎一致。
this代表的是本类对象的引用。
super代表的是父类对象的引用
2,子父类中的函数。
当子类出现和父类一模一样的函数时,
当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。
这种情况是函数的另一个特性:重写(覆盖)
当子类继承父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖,保留父类的功能定义,并重写功能内容。
覆盖:
1,子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2,静态只能覆盖静态
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样
3,子父类中的构造函数。
在对子类对象进行初始化时,父类的构造函数也会运行,
那是因为子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
但子类中至少会有一个构造函数会访问父类中的构造函数。、
class Fu
{
int num =5 ;
Fu()
{
System.out.println("Fu gouzao");
}
Fu(int x)
{
System.out.println("Fu..."+x);
}
void speak()
{
System.out.println("fu run");
}
}
class Zi extends Fu
{
int num = 10 ;
Zi()
{
//super(); 子类的构造函数在第一行都会有一个默认的super()
super(2);//可以手动初始化
System.out.println("Zi gouzao");
}
Zi(int x)
{
//super();
System.out.println("Zi....."+x);
}
void show()
{
System.out.println(super.num);//若不写super 则默认为this
}
void speak()//覆盖父类中的方法 必须和父类是一摸一样的,权限要大于等于父类的,private除外
{
System.out.println("Zi run");
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
//Zi z = new Zi();
Zi z1 = new Zi(66);
//z.show();
z1.speak();
}
}
final 可以用来修饰类,函数和变量
1.修饰类后 该类不能被继承
2.修饰函数(方法)后,该方法不能被复写
3.修饰变量后,该变量就会成为一个常量 一般定义 public static final double MY_PI = 3.14;
4.在局部的内部类中,只能访问该局部被final修饰的局部变量。
抽象类的特点;
1.抽象方法所在的类是抽象的
2.抽象类和抽象方法必须用abstract 所修饰
3.抽象类不能用new建立对象,因为没有方法实体,并且建立对象毫无意义
4.抽象类中的方法必须被复写后,然后通过建立子类对象来使用
5.复写抽象类中的方法只要有一个没有被复写,那么其子类就是抽象的
abstract 关键字,和哪些关键字不能共存。
final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。但抽象方法运行没意义。
abstract class Student
{
abstract void study();
void sleep()
{
System.out.println("躺着");
}
}
class Student1 extends Student
{
void study()
{
System.out.println("study");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
new Student1().study();
}
}
java中实现多继承是通过接口来实现的,因为接口中的方法都没有方法实体
接口和接口之间是继承关系,可以是多继承
类与类之间是是继承关系,可以多层继承,不能多继承
类与接口之间是实现关系,可以多实现,因为接口中的方法都没有方法实体
interface 中的变量都是常量 固定格式为: public static final int x = 4 ;
方法都是抽象的 固定格式为 public abstract void();
接口不能创建对象,因为它是抽象的,它必须被其子类把所有的抽象方法复写后然后通过子类来创建对象
接口主要是用来实现功能的扩展
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(){}
}
interface A
{
void methodA();
}
interface B extends A
{
void methodB();
}
interface C extends B,A
{
void methodC();
}
class D implements C
{
public void methodA(){}
public void methodC(){}
public void methodB(){}
}
抽象类和接口的异同:
相同点:
1,都可以在内部定义抽象方法。
2,通常都在顶层。
3,都不可以实例化,都需要子类来实现。
不同点:
1,抽象类中可以定义抽象方法和非抽象方法,而接口中只能定义抽象方法。
2,接口的出现可以多实现。抽象类只能单继承。
也就是说:接口的出现避免了单继承的局限性。
3,继承和实现的关系不一致。
多态:
1.多态的体现
父类的引用指向子类的对象,父类的引用可以接收子类的对象
2.多态的前提
类与类之间必须有关系,要么继承,要么实现,要有覆盖的动作
3.多态的好处
大大的提高了程序的扩展性
4.多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员
5.多态的应用
多态中,非静态成员函数的特点
在编译时期参阅引用变量所属类中有没有这个方法,如果有,则编译通过,若没有,则编译失败,在运行时期参阅对象所属类中的方法
在编译时期看左边,运行时期看右边
多态中,静态成员函数的特点
无论编译和运行都看左边所属的类(因为静态是随着类的加载而加载,优先于对象而存在,可以直接类名调用,并且是静态绑定在类中)
多态中,静态和非静态成员变量的特点
无论编译和运行都看左边所属的类
/*
主板的多态事例
有一个主板,它应该具有一些功能,但为了功能有更好的扩展性,
我们会定义一个接口,主板可以使用这个接口,然后符合这个接口的功能都能实现
*/
class MainBoard
{
public void run()
{
System.out.println("MainBoard run");
}
public void usePCI(PCI p)//主板使用接口 接口类型的引用指向子类对象,多态
{
//存在对象时,才具有开关的功能
if(p!=null)
{
p.open();
p.close();
}
}
}
/*
定义一个接口,它只有开和关的动作
这样主板的程序不需要改变,接口也是固定的
若需要扩展新的功能,只需实现接口即可
*/
interface PCI
{
public void open();
public void close();
}
class SoundCard implements PCI
{
public void open()
{
System.out.println("SoundCard run");
}
public void close()
{
System.out.println("SoundCard close");
}
}
class Mouse implements PCI
{
public void open()
{
System.out.println("Mouse open");
}
public void close()
{
System.out.println("Mouse close");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
MainBoard mb = new MainBoard();
mb.run();
mb.usePCI(new SoundCard());
mb.usePCI(new Mouse());
}
}
内部类:
1.内部类可以直接访问外部类中的成员,包括私有,之所以能够访问,就是因为外部类的成员前省略了 外部类名.this
2.外部类想要访问内部类中的成员必须建立内部类中的对象
3.别的类想要访问内部类,应该先要建立外部类的对象,然后在建立内部类的对象
格式为:外部类名.内部类名 x = new 外部类名().new 内部类名();
内部类在外部类的成员位置上,所以可以被static private 修饰
静态内部类访问外部类中的成员时,外部类的成员必须是静态的
外部类中的静态方法访问内部类时,内部类也必须是静态的
内部类中的方法若是静态的则内部类必须是静态的
一个静态内部类访问另一个内部类时,那个内部类必须是静态的
内部类定义在局部时。
1.不可以被成员修饰符所修饰,如 static private 等
2.可以直接访问外部类中的成员,因为还带有外部的引用
但是不可以访问它所在局部类中的变量,只能访问被final修饰的局部变量
class Outer
{
private static int x = 1;
void show()
{
new Inner().function();
System.out.println("Outer run..."+x);
}
static class Inner
{
//int x = 2 ;
void function()
{
//int x = 3 ;
System.out.println("Inner run....."+x);
}
}
static class Inner2
{
void show1()
{
new Inner().function();
System.out.println("Inner2 run");
}
}
public static void method()
{
new Inner().function();
System.out.println("method run");
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
new Outer.Inner2().show1();//内部类是静态但其方法不是静态时这样调用
//new Outer().new Inner2().show1();//内部类不是静态并且方法也不是静态时这样调用
//Outer.Inner.function();//内部类是静态的并且其方法也是静态的
}
}
匿名内部类:
1,匿名内部类其实就是内部类的简写格式。
2,定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
3,匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。可以理解为带内容的对象。
5,匿名内部类中定义的方法最好不要超过3个。
interface Inner
{
void method();
}
class InnerClassTest
{
public static void main(String[] args)
{
Test.function().method();
/*
1.Test可以直接调用function(),说明在Test类中有一个静态的function()方法
2.Test.function()可以调用method(),首先说明Test类实现了Inner接口,复写了method(),并且Test.function() 还是一个对象并且对象是Inner的子类
*/
}
}
class Test
{
//不用匿名内部类补足代码
public static Inner function()
{
return new Inter();
}
static class Inter implements Inner
{
public void method()
{
System.out.println("jajaja");
}
}
//通过匿名内部类补足代码
/*public static Inner function()
{
return new Inner()
{
public void method()
{
System.out.println("method run");
}
};
}
*/
}
//需求:在一个方法不属于任何类的情况下调用它
class In
{
public static void main(String[] args)
{
new Object()//Object是任何类的父类
{
public void show()
{
System.out.println("show");
}
}.show();
}
}