学习面向对象第二节

本节的内容的我自己的主要难点在调用父类构造器

1.Java包

Java默认为源文件导入java.lang包下的国有类,因此前面在Java程序中使用String和System类都无需使用import来导入这些类,但对于前面介绍的数组时候提到的Array类,则必须使用import语句来导入该类。

使用import可以省略包名,但是使用import static 则可以连类名都省略

2.Java源文件的大体结构

package语句 //0个或者是1个,必须放在文件的开始

import | import static语句   //0个或多个,必须放在所有类定义之前

public classDefinition | interfaceDefinition | enumDefinition   //0个或1个public类、接口或枚举定义

classDefinition | interfaceDefinition | enumDefinition   //0个或多个普通类、接口或枚举

上面提到了接口定义、枚举定义,可以暂时把接口、枚举都当成一种特殊的类

3.Java的常用包

Java的核心类都放在java包下以及其子包下,Java扩展的许多类都放在javax包以及其子包下。这些实用类就是前面所说的API(应用接口),Oracle按这些类的功能分别放在不同的包下。下面几个包是Java的常用包

1)java.lang:这个包下包含了Java语言的核心类,如String,System,Math,和Thread类等,使用这个包下的类无需使用import导入,系统会自动导入这个包下的所有类

2)java.util:这个包下包含了Java的大量工具类\接口和集合框架类\接口,例如Arrays和list、Set等

3)java.net:这个包下包含了Java网络编程相关的类和接口

4)java.io这个包下包含了一些Java输入输出编程相关的类和接口

5)java.text:这个包下包含了一些Java格式相关的类

6)java.sql:这个包下包含了一些Java进行JDBC数据库编程的相关类和接口

7)java.awt:这个包下包含了抽象窗口集合(Abstruct Window ToolKits)的相关类\接口,这些主要用于图形用户界面(GUI)程序

8)java.swing这个包下包含了Swing图形用户界面编程的相关类\接口,这些类可用于构建平台无关GUI程序

4.深入构造器

构造器是一个特殊的方法,这个特殊的方法用于创建对象时执行初始化。构造器是创建对象的重要途径(即使使用工厂模式、反射的等方式创建对象,其实质依然是依赖于构造器),因此,Java类必须包含一个或一个以上的构造神器。

下面定义了一个自定义的构造器,通过这个构造器就可以让程序员进行及定义的初始化操。

 
 

构造器是创建Java对象的重要途径,通过new关键字调用构造器时,构造器也确实返回了该类的对象,但这个对象并不是完全由构造器负责创建的。实际上,当程序员调用这个构造器时,系统会先为对象分配内存空间,并为这个对象执行默认初始化,这个对象已经产生了——这些操作在构造器执行之前就完成了。也就是说,当系统开始执行构造器执行体之前,系统已经产生了一个对象,知识这个对象还不能被外部程序访问,只能在该构造器中通过this类引用。当构造器的执行体执行结束之后,这个对象作为构造器的返回值返回,通常还会赋给另一个引用类型变量,从而让外部程序可以访问。

构造器的重载

public class ConstructorTestOverload {
	public String name;
	public int count;
	//提供无参数的构造器
	public ConstructorTestOverload(){	
	}
	//提供两个参数的构造器
	//对该构造器返回的对象执行初始化
	public ConstructorTestOverload(String name,int count){
		this.name=name;
		this.count=count;
	}
	public static void main(String[] args){
		//通过无参构造器构造ConstructorTestOverload对象
		ConstructorTestOverload col=new ConstructorTestOverload();
		ConstructorTestOverload col2=new ConstructorTestOverload("疯狂Java讲义",9000);
		System.out.println(col.count);
		System.out.println(col2.count);
		
	}
		
}
public class Apple extends Fruit{
	public String name;
	public String color;
	public double weight;
	public Apple(){
	}
	//两个参数的构造器
	public Apple(String name,String color){
		this.name=name;
		this.color=color;
	}
	public Apple(String name,String color,double weight){
		this(name,color);
		this.weight=weight;
		//this(name,color);this调用另一个重载的构造器的时候this语句必须放在第一条语句
	}
	public static void main(String[] args){
		/*//创建Apple对象
		Apple a=new Apple();
		//Apple对象本身没有weight成员变量
		//因为Apple的父类有weight成员变量,也可以访问Apple对象的weight成员变量
		a.weight=56;
		//调用Apple的info()方法
		a.info();*/
		Apple ap1=new Apple();
		Apple ap2=new Apple("苹果","红色");
		Apple ap3=new Apple("香蕉","黄色",16);
		System.out.println(ap1.name+","+ap2.name+","+ap3.name);
	}
}

使用this调用另一个重载的构造器只能在构造器中使用,而且必须作为构造器执行体的第一条语句。使用this调用重载的构造器时,系统根据this后括号里的实参来调用形参与之对应的构造器。
5.重写父类的方法

方法的重写要遵循“两同两小一大”的原则,“两同”即方法名相同,形参列表相同;“两小”值的是子类方法返回值类型应比父类更小或者相等,“一大”指的是,子类方法的访问权限应比父类方法的访问权限更大或者相等。尤其是要指出的是,覆盖方法和被覆盖的方法要么都类方法,要么都是实例方法,不能一个类方法,一个是实例方法。

如果父类有private访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也就是无法重写该方法。如果子类中定义了一个与父类private方法具有相同方法名、相同的形参列表、相同返回值类型的方法,依然不是重写,知识在子类中定义了一个新方法。

5.1super限定

如果子类定义了和父类同名的实例变量,则会发生子类的实例变量隐藏父类实例变量的情形。在正常情况下,子类定义的方法直接访问该实例变量默认会访问到子类中定义的实例变量,无法访问到父类的子类中被隐藏的实例变量。在子类定义的实例方法中可以e通过super来访问父类中被影藏的实例变量,如下代码:

class BaseClass{
	public int a=5;
	public void fly(){
		System.out.println("父类的方法被调用");
	}
}
public class SubClass extends BaseClass{
	public int a=7;
	public void fly(){
		System.out.println("子类的方法被调用");
	}
	public void accessOwner(){
		System.out.println(a);
	}
	public void accessBase(){
		System.out.println(super.a);
	}
	public void callOverridedMethod(){
		super.fly();
	}
	public static void main(String[] agrs){
		SubClass sc=new SubClass();
		sc.accessOwner();
		sc.accessBase();
		sc.fly();
		sc.callOverridedMethod();
	}
}

需要注意的是super关键字并不能够访问父类中被隐藏的成员变量或者私有的方法(private)只能访问public修饰方法或者是变量。而且super关键字不能出现在static方法的程序片中。

6.调用父类构造器

class BaseClass{
	public int a=5;
	public void fly(){
		System.out.println("父类的方法被调用");
	}
}
public class SubClass extends BaseClass{
	public int a=7;
	public void fly(){
		System.out.println("子类的方法被调用");
	}
	public void accessOwner(){
		System.out.println(a);
	}
	public void accessBase(){
		System.out.println(super.a);
	}
	public void callOverridedMethod(){
		super.fly();
	}
	public static void main(String[] agrs){
		SubClass sc=new SubClass();
		sc.accessOwner();
		sc.accessBase();
		sc.fly();
		sc.callOverridedMethod();
	}
}

class Creature{
	public Creature(){
		System.out.println("Creature无参数构造器");
	}
}
class Animal extends Creature{
	public Animal(String name){
		System.out.println("Animals带一个参数的构造器"+"动物的名字"+name);
	}
	public Animal(String name,int age){
		//使用this调用同一个重载构造器
		this(name);
		System.out.println("Animal带两个参数的构造器,"+"其age为"+age);
	}
}
public class Wolf extends Animal{
	public Wolf(){
		//显示调用父类有两个参数的构造器
		super("灰太狼",3);
		System.out.println("Wolf无参数构造器");
	}
	public static void main(String[] args){
		new Wolf();
	}
}
其中父类中已经定义了一个构造方法,在子类中必须要定义一个构造方法,不管构造方法中是否有参数,都需要在子类中创建一个构造方法,否则会报错,自己的理解,因为如果没有在的子类中创建一个构造方法,那么就无法根据构造方法创建,必须要在子类中创建一个构造器。

7.多态

如果编译时类型和运行时的类型不一致,就可以出现所谓的多态。

class BaseClass{
    public int book=6;
    public void base(){
        System.out.println("父类的普通方法");
    }
    public void test(){
        System.out.println("父类被覆盖的方法");
    }
    public void Test(){
        System.out.println("测试猜想是否正确");
    }
}
public class SubClass extends BaseClass{
    //重新定义一个book实例变量隐藏父类的book实例变量
    public String book="轻量级Java EE企业应用实战";
    public void test(){
        System.out.println("子类覆盖父类的方法");
    }
    public void sub(){
        System.out.println("子类的普通方法");
    }
    public static void main(String[] args){
        //下面编译时出现类型和运行时类型完全一样,因此不存在多态
        BaseClass bc=new BaseClass();
        System.out.println(bc.book);
        //下面两次调用将执行BaseClss方法
        bc.base();
        bc.test();
        //下面编译时出现类型和运行类型完全一样,因此不存在多态
        SubClass sc=new SubClass();
        //输出"轻量级Java EE企业应用实战"
        System.out.println(sc.book);
        //下面调用从父类继承的base方法
        sc.base();
        //下面调用执行当前类的test方法
        sc.test();
        //下面编译时类型和运行时类型不一样,多台发生
        BaseClass ploymophicBc=new SubClass();
        //输出6表明访问的是父类对象的实例变量
        //子类的不可以new一个父类
        //SubClass ploy=new BaseClass();
        //允许将子类的指针赋值给父类
        System.out.println(ploymophicBc.book);
        //下面调用将执行从父类继承的base()方法
        ploymophicBc.base();
        //下面调用将执行当前类的test方法
        ploymophicBc.test();
        //因为ploymophicBc编译时类型是BaseClass
        //BaseClass类型没有提供sub(),方法所以下面的代码会出现错误
        //ploymophicBc.sub();
        ploymophicBc.Test();
    }
}

上面程序的main()方法提供了三个引用变量,对于前两个引用变量bc和sc,他们编译时类型和运行类型完全相同,因此调用它们的成员变量和方法非常正常,完全没有任何问题。但第三个引用变量ploymophinBc则比较特殊,他的编译时类型是BaseClass,而运行类型是SubClass,当调用该引用变量的test()方法时,实际执行的是SubClass类中覆盖后的test()方法,这就实现多态了。

因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcasting),向上转型由系统自动完成。





猜你喜欢

转载自blog.csdn.net/xiao_chainiao/article/details/75143825