一、成员变量与局部变量
二、类的继承
语法:
修饰符 class SubClass extends SuperClass
{
// 类的定义部分
}
Java类具有单继承的特点,每个子类只有一个直接父类,但注意“直接”两字,Java类可以有许多间接父类,但直接父类只能有一个。
例如:
java.lang.Object
java.util.AbstractCollection
java.util.AbstractList
java.util.ArrayList
而:class SubClass extends Base1, Base2, Base3{} 这样写法报编译错误。
继承示例:
1、继承父类的属性和方法
Fruit.java
package extendsdemo;
public class Fruit {
public double weight;
public void info(){
System.out.println("父类输出:我是一个水果!重量" + this.weight + "g!");
}
}
Apple .java
package extendsdemo;
public class Apple extends Fruit {
// do nothing
}
MainClass.java
package mainclasspackage;
import extendsdemo.Apple;
public class MainClass {
public static void main(String[] args) {
Apple apple = new Apple(); // Apple 类本身是一个空类,但它继承了父类的weight属性和info方法。
apple.weight = 56D;
apple.info();
}
}
2、重写父类的属性和方法(重写也称为覆盖Override)
父类:Bird
package extendsdemo;
public class Bird {
public void fly(){
System.out.println("我在填空自由自在的飞翔!");
}
}
子类:Ostrich
package extendsdemo;
public class Ostrich extends Bird {
public void fly(){
System.out.println("我只能在地上奔跑!");
}
}
3、方法的重载遵循“两同、两小、一大”规则
“两同”:方法名相同,形参列表相同;
“两小”:指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常应比父类方法声明抛出的异常类更小或相等;
“一大”:子类的方法的访问权限应比父类方法更大或相等;
尤其需要指出的,覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
下面示例都不是重写,子类的方式是不同于父类的另一个方法:
父类:BaseClass.java
package extendsdemo;
public class BaseClass {
public Ostrich findBird(String str1, int i1){
Ostrich ostrich = new Ostrich();
return ostrich;
}
}
子类:SubClass.java
package extendsdemo;
public class SubClass extends BaseClass {
/**
* 1、形参列表不同
* 2、子类findBird方法的返回值 > 父类findBird方法的返回值
* 3、子类findBird方法的访问修饰符 < 父类findBird方法的访问修饰符
* @param str1
* @return
*/
protected Bird findBird(String str1){
Bird bird = new Bird();
return bird;
}
}
4、子类中如何调用父类的成员属性、成员方法、构造函数——super关键字
package extendsdemo;
public class Ostrich extends Bird {
/**
* this 指的是当前对象
* super 指的是当前对象的父类对象
* 在构造函数中 this、super必须卸载构造函数的第一行
* 在普通方法中,可以使用super.方法名 调用父类的方法
* super与this相同,不能与static关键字同时使用
*/
public Ostrich(){
// super();
this("");
System.out.println("我是子类的构造函数");
}
public Ostrich(String str){
// super();
System.out.println("我是子类被重载的构造函数,我有一个形参");
}
public void fly(){
System.out.println("我只能在地上奔跑!");
super.fly();
}
public static void test(){
this.fly(); // 这里会报编译错误
super.fly(); // 这里会报编译错误
}
}
子类构造函数调用父类构造函数的顺序:
为了保证父类良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:
class Base{
public Base(){
test();
}
public void test(){ // ① 号 test 方法
System.out.println("将被子类重写的方法");
}
}
class Sub extends Base{
private String name;
public void test(){ // ② 号 test 方法
System.out.println("子类重写父类的方法,其 name 字符串的长度" + name.length());
}
public static void main(String[] args){
// 下面代码会引发空指针异常
Sub s = new Sub();
}
}
当系统试图创建 Sub 对象时,同样会先执行其父类的构造器,如果父类的构造器调用了被其子类重写的方法,则变成了调用被子类重写后的方法。当创建 Sub 对象时,会先执行 Base 类中的 Base 构造器。而 Base 构造器中调用了 test 方法——并不是① 号 test 方法,而是调用了② 号 test 方法,此时 Sub 对象的 name 属性是 null,因此将引发空指针异常。
例如:
BaseClass1:
package baseclasspackage;
public class BaseClass1{
/**
* 只提供一个构造子,并且该构造子属私有 private 类型,该类无法被继承,
* 且其他类也无法在调用该构造子,所以需要通过提供一个静态工厂方法提供
* 该类的实例
*/
private BaseClass1(){}
/**
* 静态工厂方法获得类的实例
* @return
*/
public static BaseClass1 getInstance(){
return new BaseClass1();
}
}
SubClass1:
package subclasspackage;
import baseclasspackage.BaseClass1;
public class SubClass1 extends BaseClass1 { // 发生编译错误,无法继承 BaseClass1
}
BaseClass2 :
package baseclasspackage;
/**
* 使用 final 关键字修饰的类,无法被继承
* @author Administrator
*/
public final class BaseClass2 {
}
BaseClass2 :
package subclasspackage;
import baseclasspackage.BaseClass2;
public class SubClass2 extends BaseClass2 { // 发生编译错误
}
如何实例化BaseClass1和BaseClass2:
MainClass:
package mainclasspackage;
import baseclasspackage.BaseClass1;
import baseclasspackage.BaseClass2;
public class MainClass {
public static void main(String[] args) {
BaseClass1 b1 = new BaseClass1(); // 发生编译错误,无法调用私有的构造函数
BaseClass1 b1_ = BaseClass1.getInstance(); // 通过调用静态工厂方法,获得BaseClass1的实例
BaseClass2 b2 = new BaseClass2(); // 使用 final 关键字修饰的 BaseClass2 在实例化上与普通类相同
}
}
6、利用组合实现复用
示例:
Animal:
package compositedemo;
public class Animal {
private void beat(){
System.out.println("心脏跳动……");
}
public void breath(){
beat();
System.out.println("吸一口气、呼一口气、呼吸中……");
}
}
Bird :
package compositedemo;
public class Bird {
//将原来的父类嵌入原来的子类,作为子类的一个组合成分
private Animal a;
public Bird(Animal a)
{
this.a = a;
}
//重新定义一个自己的breath方法
public void breath()
{
//直接复用Animal提供的breath方法来实现Bird的breath方法。
a.breath();
}
public void fly()
{
System.out.println("我在天空自在的飞翔...");
}
}
Wolf :
package compositedemo;
public class Wolf {
//将原来的父类嵌入原来的子类,作为子类的一个组合成分
private Animal a;
public Wolf(Animal a)
{
this.a = a;
}
//重新定义一个自己的breath方法
public void breath()
{
//直接复用Animal提供的breath方法来实现Bird的breath方法。
a.breath();
}
public void run()
{
System.out.println("我在陆地上的快速奔跑...");
}
}
MainClass:
package compositedemo;
public class MainClass {
public static void main(String[] args)
{
//此时需要显式创建被嵌入的对象
Animal a1 = new Animal();
Bird b = new Bird(a1);
b.breath();
b.fly();
//此时需要显式创建被嵌入的对象
Animal a2 = new Animal();
Wolf w = new Wolf(a2);
w.breath();
w.run();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
作业部分:
新建一个类Bird
public class Bird{
// 属性1,实例属性,鸟的颜色 String
// 属性2,实例属性,鸟的种类 String
// 属性3,类属性(静态属性),鸟的眼睛的数量 int
// 属性4,类属性(静态属性),鸟的翅膀的数量 int
// 属性5,实例属性,是否会飞 boolean
/*
* 为所有的实例属性编写getter、setter
*/
// 第一个构造函数:空的构造函数
// 第二个构造函数:为所有实例属性初始化
public Bird(String color, String sort, boolean flag){
// ...
{
// 使用静态块初始化所有类属性
static{
Bird.鸟眼睛的数量 = 2;
Bird.鸟翅膀的数量 = 2;
}
// 添加一个方法,作用是:输出Bird中的各个属性
public void showBird(){
// ...
}
}
新建一个Bird类的子类,名字叫Ostrich
public class Ostrich extends Bird{
// 覆盖直接父类(Bird)的showBird()方法
// 覆盖顶级父类(java.lang.Object)的toString()方法,方法返回值是String,字符串包含的信息:颜色、种类、是否会飞
}
MainClass{
Ostrich ostrich = new Ostrich();
ostrich.set颜色();
ostrich.set种类();
ostrich.set是否会飞();
// 修改两个静态属性,
调用Ostrich类重载之后的showBird()方法和toString()方法。
}