继承要符合的关系是:is-a,父类更通用,子类更具体.
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
final关键字:
子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
class SubClass extends SuperClass{
private int n;
SubClass(){
super(300); // 必须在子类的构造器中显式地通过super关键字调用父类的构造器(带参)
System.out.println("SubClass");
}
public SubClass(int n){
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
public class TestSuperSub{
public static void main (String args[]){
SubClass sc = new SubClass(); // new SubClass()中调用父类的带参构造器
SubClass sc2 = new SubClass(200); // 会调用父类的无参构造器
}
}
输出结果为:
SuperClass(int n)
SubClass
SuperClass()
SubClass(int n):200
到底是调用父类的带参数还是无参构造器还要看实例化对象语句。
继承的注意点:
super 关键字:
super 表示使用它的类的父类。super 可用于:
注意:super 语句必须是子类构造方法的第一条语句(必须是子类构造方法的第一句)。不能在子类中使用父类构造方法名来调用父类构造方法。 父类的构造方法不被子类继承(这也就是为什么要用super关键字)。调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super(); 作为子类构造方法的第一条语句。这会形成一个构造方法链。
注意:静态方法中不能使用 super 关键字。
如果是继承的方法,是没有必要使用 super 来调用,直接即可调用。但如果子类覆盖或重写了父类的方法,则只有使用 super 才能在子类中调用父类中的被重写的方法。
1、final 修饰类中的属性或者变量
无论属性是基本类型还是引用类型,final 所起的作用都是变量里面存放的”值”不能变。
这个值,对于基本类型来说,变量里面放的就是实实在在的值,如 1,”abc” 等。
而引用类型变量里面放的是个地址,所以用 final 修饰引用类型变量指的是它里面的地址不能变,并不是说这个地址所指向的对象或数组的内容不可以变,这个一定要注意。
例如:类中有一个属性是 final Person p=new Person(“name”); 那么你不能对 p 进行重新赋值,但是可以改变 p 里面属性的值 p.setName(‘newName’);
final 修饰属性,声明变量时可以不赋值,而且一旦赋值就不能被修改了。对 final 属性可以在三个地方赋值:声明时、初始化块中、构造方法中,总之一定要赋值。
2、final修饰类中的方法
作用:可以被继承,但继承后不能被重写。
3、final修饰类
作用:类不可以被继承。
在编写代码要注意:
执行父类构造函数的语句只能放在函数内语句的首句,不然会报错。
在继承关系中,在调用函数(方法)或者类中的成员变量时,JVM(JAVA虚拟机)会先检测当前的类(也就是子类)是否含有该函数或者成员变量,如果有,就执行子类中的,如果没有才会执行父类中的。如下:
public class Start
{
public static void main(String[] args)
{
Cat cat=new Cat("Jack","黑色");
cat.eat();
cat.run();
cat.sleep();
}
}
class Animal
{
String name;
public Animal(){}//必须要写这个构造函数,不然Cat类的代码会出错
public Animal(String name)
{
this.name=name;
}
void eat()
{
System.out.println(name+"正在吃");
}
void run()
{
System.out.println(name+"正在奔跑");
}
void sleep()
{
System.out.println(name+"正在睡觉");
}
}
class Cat extends Animal
{
String color;
public Cat(String name,String color)
{
this.name=name;
this.color=color;
}
void eat()
{
System.out.println(color+"的"+name+"正在吃鱼");
}
}
运行结果如下:
黑色的Jack正在吃鱼
Jack正在奔跑
Jack正在睡觉
当子类出现与父类一样的函数时,这个被称为 重写 也叫 覆盖
Object类是所有类的直接父类或间接父类,也就是说是所有类的根父类,这个可以运用于参数的传递
如下:
public class Start
{
public static void main(String[] args)
{
A a=new A();
B b=new B();
C c=new C();
D d=new D();
speak(a);
speak(b);
speak(c);
speak(d);
}
// instanceof 关键字是用于比较类与类是否相同,相同返回true,不同返回false
//当你不清楚你需要的参数是什么类型的,可以用Object来代替,Object可以代替任何类
static void speak(Object obj)
{
if(obj instanceof A)//意思是:如果参数是 A 类,那么就执行一下语句
{
A aobj=(A)obj;//这里是向下转换,需要强制转换
aobj.axx();
}
else if(obj instanceof B)
{
B bobj=(B)obj;
bobj.bxx();
}
else if(obj instanceof C)
{
C cobj=(C)obj;
cobj.cxx();
}
}
}
//这里举了四个类,他们的函数都不同,但都是 Object 类的子类
class A
{
void axx()
{
System.out.println("Good morning!");
System.out.println("This is A");
}
}
class B
{
void bxx()
{
System.out.println("Holle!");
System.out.println("This is B");
}
}
class C
{
void cxx()
{
System.out.println("Look!");
System.out.println("This is C");
}
}
class D
{
void dxx()
{
System.out.println("Oh!Bad!");
System.out.println("This is D");
}
}
运行结果:
Good morning!
This is A
Holle!
This is B
Look!
This is C
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
在面向对象原则里,重写意味着可以重写任何现有方法。
在编译阶段,只是检查参数的引用类型。然而在运行时,Java虚拟机(JVM)指定对象的类型并且运行该对象的方法。
思考以下例子:
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
public void bark(){
System.out.println("狗可以吠叫");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
b.bark();
}
}
TestDog.java:30: cannot find symbol
symbol : method bark()
location: class Animal
b.bark();
^
该程序将抛出一个编译错误,因为b的引用类型Animal没有bark方法
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则
重写与重载之间的区别:
总结:
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
重载与重写的简明理解:
重载反映的是“随机应变”. 同样一项功能, 根据数据类型的不同, 采用不同的处理方式. 比如, 同样是吃饭, 去高档餐厅吃西餐, 你会西装革履, 但是去吃火锅, 穿便装就比较合适.
重写反映的是“父子差异”. 你”继承”了父亲吃火锅的爱好, 但是吃同一份鸳鸯锅(注意, 数据类型相同) , 你喜欢涮红汤, 你父亲喜欢涮清汤.
补充:父类声明变量指向子类实例,该父类变量不能调用父类不存在的变量和方法,否则会编译错误
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public int age;
public void move(){
age = 10;
System.out.println("狗可以跑和走");
}
public void bark(){
System.out.println("狗可以吠叫");
}
}
public class TestOverride{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
// b.age;//去掉前注释符号,会编译错误
// b.bark();//去掉前注释符号,会编译错误
}
}
摘自:
http://www.runoob.com/java/java-override-overload.html
http://www.runoob.com/java/java-inheritance.html