Java(五)继承

1、类、超类和子类

     继承的格式:

     class 子类名 extends 超类名

     {

          添加方法和域

     }

     与C++不同,java中,所有的继承都是公有继承。

     关键字 extends 表明正在构造的新类派生于一个已存在的类,已存在的类被称为超类、基类或父类,新类被称为子类、派生类或孩子类。

     如果我们需要调用超类中的一个方法,而不是当前类的这个方法,可以使用在方法前加上 关键字 super 来解决。

             super 在构造器中的应用:

public Manager(String n,double s,int year,int month,int day)
{
	super(n,s,year,month,day);
	bonus = 0;
}

           这里的关键字 super 具有不同的含义,语句:super(n,s,year,month,day);  是调用超类中含有对应参数的构造器 的简写形式

           使用 super 调用构造器的语句必须是子类构造器的第一条语句。

           如果子类的构造器没有显示的调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器,如果超类没有不带参数的构造器,则报错。

          实例:

import java.util.*;
public class ManagerTest
{
	public static void main(String[] args)
	{
		Manager boss = new Manager("Mr boss",70000,1970,1,5);
		boss.setBonus(5000);
		
		Employee[] staff = new Employee[3];
		staff[0] = boss;
		staff[1] = new Employee("Ai",40000,1990,12,15);
		staff[2] = new Employee("Bi",30000,1991,1,2);
		
		for (Employee e : staff)
			System.out.println("name="+e.getName()
					+",salary="+e.getSalary());
	}
}

class Employee//超类
{
	public Employee(String n,double s,int year,int month,int day)
	{
		name = n;
		salary = s;
		GregorianCalendar calendar = new GregorianCalendar(year,month-1,day);
		hireDay = calendar.getTime();
	}
	
	public String getName()
	{
		return name;
	}
	
	public double getSalary()
	{
		return salary;
	}
	
	public Date getHireDay()
	{
		return hireDay;
	}
	
	public void raiseSalary(double byPercent)
	{
		double raise = salary * byPercent / 100;
		salary += raise;
	}
	
	private String name; 
	private double salary;
	private Date hireDay;
}
	
class Manager extends Employee //子类
{
	public Manager(String n,double s,int year,int month,int day)
	{
		super(n,s,year,month,day);
		bonus = 0;
	}
	
	public double getSalary()//子类中重载的方法
	{
		double baseSalary = super.getSalary();//调用超类中的方法
		return baseSalary + bonus;
	}
	
	public void setBonus(double b)
	{
		bonus = b;
	}
	
	private double bonus;
}
	

运行结果:

         一个对象变量可以引用多种实际类型的现象被称为多态,在运行时能够自动地选择调用哪个方法的现象称为动态绑定。

         java 中,不需要将方法声明为虚拟方法,动态绑定时默认的处理方式。


      (1)多态

               程序中出现超类对象的任何地方都可以用子类对象置换

      (2)动态绑定

               调用过程:1)编译器查看对象的声明类型和方法名

                                 2)编译器查看调用方法时提供的参数类型。如果在所有重载中找到一个完全参数类型匹配的,就选择这个(称为重载解析)。

                                 3)如果是 private 方法、static 方法、final 方法或者构造器,那么编译器将可以准确地知道应该调用哪个方法(称为静态绑定)。

                                 4)当程序运行时,并且采取动态绑定调用方法时,虚拟机一定调用最合适的那个类的方法。     

     (3)阻止继承:final 类和方法

              不允许扩展的类被称为 final 类(C++中则是使用虚基类)。定义时使用 final 修饰符修饰,放在 class 前面:final class 类名{ }

              类中的方法也可以声明为 final,子类就不能覆盖整个方法(final 类中的所有方法自动称为 final 方法,不包括域)

     (4)抽象类

              为了提高程序的清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象的。

              除了抽象方法之外,抽象类还可以包含具体数据和具体方法,例如下面的 Person 类还保持这name 和 getName 方法

              实例:

import java.util.*;
public class PersonTest 
{
	public static void main(String[] args)
	{
		Person[] people = new Person[2];
		
		people[0] = new Employee("Ai",5000,1992,1,15);
		people[1] = new Student("GG","computer science");
		
		for (Person p : people)
			System.out.println(p.getName()+","+p.getDescription());
	}
}

abstract class Person //抽象超类
{
	public Person(String n)
	{
		name = n;
	}
	
	public abstract String getDescription();//抽象方法
	
	public String getName()
	{
		return name;
	}
	
	private String name;
}

class Employee extends Person //子类
{
	public Employee(String n,double s,int year,int month,int day)
	{
		super(n);//调用超类构造器
		salary = s;
		GregorianCalendar calendar = new GregorianCalendar(year,month-1,day);
		hireDay = calendar.getTime();
	}
	
	public double getSalary()
	{
		return salary;
	}
	
	public Date getHireDay()
	{
		return hireDay;
	}
	
	public String getDescription()
	{
		return String.format("an employee with a salary of $%.2f",salary);
	}
	
	public void raiseSalary(double byPercent)
	{
		double raise = salary * byPercent / 100;
		salary += raise;
	}
	
	private double salary;
	private Date hireDay;
}

class Student extends Person //子类
{
	public Student(String n, String m)
	{
		super(n);// 调用超类构造器
		major = m;
	}
	
	public String getDescription()
	{
		return "a student majoring in " + major;
	}
	
	private String major;
}

          运行结果:

         许多程序员认为,在抽象类中不能包含具体方法,建议尽量将通用的域和方法(不管是否是抽象的)放在超类(不管是否是抽象的)中。

         扩展抽象类可以有两种选择:一种是在子类中定义部分抽象方法或抽象方法也不定义,这样就必须将子类也标记为抽象类;

                                                       另一种是定义全部的抽象方法,这样子类就不是抽象类了,如 Employee 和 Student 类。

         类即使不含抽象方法,也可以将类声明为抽象类。

        抽象类不能被实例化,就是说,如果将一个类声明为 abstruct,就不能创建这个类的对象,但是可以创建一个具体子类的对象。

        需要注意,可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。如:Person p = new Student("Haha", "Economics");

        在 C++ 中,没有提供用于表示抽象类的关键字,而是有一种在尾部用 = 0 标记的抽象方法,称为纯虚函数,只要含有一个纯虚函数,就是抽象类

       (5)访问修饰符

                java 用于控制可见性的 4 个访问修饰符:

                1)仅对本类可见:private

                2)对所有类可见:public

                3)对本包和所有子类可见:protected

                4)对本包可见:默认,即没有标明任何修饰符的情况,这是一种不太受欢迎的形式

2、Object:所有类的超类

      Object 类是 java 中所有类的最终祖先,在 java 中每个类都是由它扩展而来的。

      可以使用 Object 类型的变量引用任何类型的对象(当然只能用于作为各种值的通用持有者),如:

Object obj = new Employee("Ai",50000);

      在 java 中,只有基本类型不是对象,所有的数组类型,不管是对象数组还是基本类型的数组都扩展于 Object 类。

     (1)equals 方法

              Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。

              equals 具有 自反性、对称性、传递性、一致性、对于任意非空引用 x,x.equals(null) 返回 false 等特性

              在子类中定义 equals 方法时,首先调用 超类的 equals。

              下面是编写一个完美的 equals 方法的建议

               1)显示参数命名为 otherObject,稍后需要将它转换成另一个叫做 other 的变量

               2)检测 this 与 otherObject 是否引用同一个对象

                     if ( this == otherObject ) return true ;

              3)检测 otherObject 是否为 null,若为空,返回 false。

                    if ( otherObject == null ) return false ;

              4)比较 this 与 otherObject 是否属于同一个类。如果 equals 的语义在每个子类中有所改变,

                    就使用 getClass 检测:

                    if ( getClass() != otherObject.getClass() ) return false ;

                    如果所有的子类都拥有统一的语义,就使用 instanceof 检测:

                    if ( !(otherObject instanceof ClassName ) ) return false;

              5)将 otherObject 转换成相应的类类型变量:

                    ClassName other = ( ClassName ) otherObject

              6)现在开始对所有需要比较的域进行比较了。使用 == 比较基本类型域,使用 equals 比较对象域,如果都匹配返回 true,否则返回 false

                    return field1 == other.field1 && field2.equals( other.feild2 ) && ... ;

                    如果是子类中重新定义 equals,就要在其中包含调用 super.equals( other )

           对于数组类型的域,可以使用静态的 Arrays.equals 方法检测相应的数组元素是否相等。

                 

           可以使用 @Override 对覆盖超类的方法进行标记如:@Override public boolean equals( Object other)

                                              如果出现了错误,并且正在定义一个新方法,编译器就会报错。

             

     (2)hashCode 方法

              散列码(hash code)是由抽象导出的一个整型值。不同的对象,散列码基本上不会相同。

              由于 hashCode 方法定义在 Object 类中,因此每个对象都有一个默认的散列码。其值为对象的存储地址。

              如果重新定义 equals 方法,就必须重新定义 hashCode 方法。

              hashCode 方法应该返回一个整型数值(可以是负数)

     (3)toString 方法

              用于返回表示对象值的字符串。

              绝大多数的 toString 方法的格式:类名,随后是一对方括号括起来的域值。

              强烈建议为自定义的每一个类都增加 toString 方法对默认进行覆盖,不仅自己受益,所有使用这个类的人都会受益。

      实例:Employee 类和 Manager 类的 equals、hashCode 和 toString 方法

import java.util.*;
public class EqualsTest 
{
	public static void main(String[] args)
	{
		Employee alice1 = new Employee("Ai",75000,1987,1,12);
		Employee alice2 = alice1;
		Employee alice3 = new Employee("Ai",75000,1987,1,12);
		Employee bob = new Employee("Bob",50000,1989,2,5);
		
		System.out.println("alice1 == alice2: "+(alice1 == alice2));
		System.out.println("alice1 == alice3: "+(alice1 == alice3));
		System.out.println("alice1.equals(alice3): "+alice1.equals(alice3));
		System.out.println("alice1.equals(bob): "+alice1.equals(bob));
		System.out.println("bob.toString(); "+bob);
		
		Manager carl = new Manager("Ci",80000,1987,1,12);
		Manager boss = new Manager("Ci",80000,1987,1,12);
		boss.setBonus(5000);
		
		System.out.println("boss.toString(): "+boss);
		System.out.println("carl.equals(boss): "+carl.equals(boss));
		System.out.println("alice1.hashCode(): "+alice1.hashCode());
		System.out.println("alice3.hashCode(): "+alice3.hashCode());
		System.out.println("bob.hashCode(): "+bob.hashCode());
		System.out.println("carl.hashCode(): "+carl.hashCode());
	}
}

class Employee //超类
{
	public Employee(String n,double s,int year,int month,int day)
	{
		name = n;
		salary = s;
		GregorianCalendar calendar = new GregorianCalendar(year,month-1,day);
		hireDay = calendar.getTime();
	}
	
	public String getName()
	{
		return name;
	}
	
	public double getSalary()
	{
		return salary;
	}
	
	public Date getHireDay()
	{
		return hireDay;
	}
	
	public String getDescription()
	{
		return String.format("an employee with a salary of $%.2f",salary);
	}
	
	public void raiseSalary(double byPercent)
	{
		double raise = salary * byPercent / 100;
		salary += raise;
	}
	
	public boolean equals(Object otherObject)
	{
		if(this == otherObject) return true;//快速的检测,看对象是否相等
		if(otherObject == null) return false;//如果是空,返回 false
		if(getClass() != otherObject.getClass()) return false;//比较类是否相同,类不同,则他们不相同
		Employee other = (Employee) otherObject;//知道非空后,转换成相应的变量
		return name.equals(other.name) && salary == other.salary//对域进行比较
				&& hireDay.equals(other.hireDay);
	}
	
	public int hashCode()
	{
		return 7*name.hashCode()+11*new Double(salary).hashCode()+13*hireDay.hashCode();
	}
	
	public String toString()
	{
		return getClass().getName()+"[name="+name+",salary="
				+salary+",hireDay="+hireDay+"]";
	}
	
	private String name;
	private double salary;
	private Date hireDay;
}

class Manager extends Employee //子类
{
	public Manager(String n,double s,int year,int month,int day)
	{
		super(n,s,year,month,day);// 调用超类构造器
		bonus = 0;
	}
	
	public double getSalary()
	{
		double baseSalary = super.getSalary();
		return baseSalary+bonus;
	}
	
	public void setBonus(double b)
	{
		bonus = b;
	}
	
	public boolean equals(Object otherObject)
	{
		if(!super.equals(otherObject))return false;//检测是否是属于同一个类
		Manager other = (Manager) otherObject;
		return bonus == other.bonus;
	}
	
	public int hashCode()
	{
		return super.hashCode() + 17 * new Double(bonus).hashCode();
	}
	
	public String toString()
	{
		return super.toString()+"[bonus="+bonus+"]";
	}
	
	private double bonus;
}
    

        运行结果:


       

3、泛型数组列表

      java 中解决数组大小等相关问题最简单的方法就是使用 ArrayList 类。

      它使用起来有点像数组,但在添加或删除元素时,具有自动调节数组容量的功能。

      ArrayList 是一个采用类型参数的泛型类,为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来

加在后面,例如:ArrayList<Employee>。 下面是声明和构造一个保存 Employee 对象的数组列表:

ArrayList<Employee> staff = new ArrayList<Employee>();

//还可以把初始容量传递给构造器
ArrayList<Employee> staff = new ArrayList<Employee>(100);//分配一个包含100个对象的内部数组

     Java SE 5.0 以前的版本没有提供泛型类,而是有一个 ArrayList 类,没有尖括号后缀。在 java 的老版本中,程序

员使用 Vector 类实现动态数组,但现在使用 ArrayList 类更加有效。

      ArrayList<T>()  :构造一个空数组列表

      ArrayList<T>( int initialCapacity) :用指定容量(参数:最初容量)构造一个空数组列表

      boolean add( T obj ):在数组列表尾端添加一个元素,永远返回 true。数组满时,会自动创建一个更大的数组。

      int size():返回存储在数组列表中的当前元素数量,等价于数组 a 的 a.length

      void ensureCapacity( int capacity ):确保数组列表在不重新分配存储空间的情况下就能够保存给定数量的元素

                                    参数:需要存储的容量

      void trimToSize():将数组列表的存储容量削减到当前尺寸.应该在确认不会添加任何元素时,再调用此方法。

      ArrayList 类似于 C++ 的 vector 模版,vector 模版还重载了 [ ]  运算符。

      访问数组列表元素:

              使用 get 和 set 方法实现访问或改变数组元素的操作,而不是使用 [ ] 语法格式。

              要设计第 i 个元素:staff.set( i, harry ); (等价于 a[i] = harry;)

              使用 add 方法为数组添加新元素,而不要使用 set 方法,它只能替换数组中已存在的元素内容。

              获取数组列表的元素:Employee e = staff.get( i );(等价于 Employee e = a[ i ];)

              void set( int index, T obj):设置数组列表指定位置的元素值

              T get(int index):获得指定位置的元素值

              void add(int index, T obj):指定位置插入元素

              T remove(int index):删除指定位置元素,并将后面的元素向前移动。返回被删除的元素

               实例:

import java.util.*;
public class ArrayListTest 
{
	public static void main(String[] args)
	{
		ArrayList<Employee> staff = new ArrayList<Employee>();
		
		staff.add(new Employee("Ai",75000,1987,1,12));
		staff.add(new Employee("Bi",50000,1988,10,1));
		staff.add(new Employee("Bob",40000,1989,2,5));
		
		for (Employee e : staff)
			e.raiseSalary(5);
		
		for (Employee e : staff)
			System.out.println("name="+e.getName()+",salary="
					+e.getSalary()+",hireDay="+e.getHireDay());
	}
}

class Employee 
{
	public Employee(String n,double s,int year,int month,int day)
	{
		name = n;
		salary = s;
		GregorianCalendar calendar = new GregorianCalendar(year,month-1,day);
		hireDay = calendar.getTime();
	}
	
	public String getName()
	{
		return name;
	}
	
	public double getSalary()
	{
		return salary;
	}
	
	public Date getHireDay()
	{
		return hireDay;
	}
	
	public void raiseSalary(double byPercent)
	{
		double raise = salary * byPercent / 100;
		salary += raise;
	}
	
	private String name;
	private double salary;
	private Date hireDay;
}

           运行结果:


4、对象包装器与自动打包

      所有的基本类型都有一个与之对应的类(称为包装器):Integer、Long、Float、Double、Short、Character、Void 和

Boolean(前六个派生于公共的超类 Number)。对象包装器类是不可变的,还是 final 的。

     实例: ArrayList<Integer> list = new ArrayList<Integer>(); //不允许写成 ArrayList<int>

     自动打包:添加元素:list.add(3); 将自动地变换成 list.add(new Integer(3))

     自动拆包:将一个 Integer 对象赋值给一个 int 值时:int n = list.get(i); 自动变换成 int n = list.get(i).intValue();


5、参数变量可变的方法

      用户自己可以定义可变参数的方法,并将参数指定为任意类型,甚至是基本类型。如:计算若干个数值的最大值

public static double max(double... values)
{
	double largest = Double.MIN_VALUE;
	for (double v : values)
		if (v > largest)
			largest = v;
	return largest;
}

     可以像下面这样调用这个方法: double m = max(3.1, 4, -5);

6、枚举类

      枚举类型实际上是一个类,实例就是里面的值。在此尽量不要构造新对象。

      比较两个枚举类型的值时,永远不需要调用 equals,而直接使用 == 。

      所有枚举类型都是 enum 类的子类,他们继承了许多方法,有 toString 和其逆方法 valueOf ,以及静态的 values 方法,下面程序

中都有介绍,还有一个 ordinal 方法,返回 enum 声明中枚举常量的位置(0开始),如 Size.SMALL.ordinal()  返回0

      实例:

import java.util.*;
public class EnumTest 
{
	public static void main(String[] args)
	{
		Size[] values = Size.values();//创建数组包含所有枚举值
		for (Size a : values)  //输出所有枚举值
			System.out.println(a);
		Scanner in = new Scanner(System.in);
		System.out.print("Enter a size:");
		String input = in.next().toUpperCase();//接收一个字符串并将字母转换为大写
		
		//toString的逆方法,将size设置成 Size.(输入的字符)
		Size size = Enum.valueOf(Size.class,input);
		System.out.println("size="+size);
		System.out.println("abbreviation="+size.getAbbreviation());
		if (size == Size.EXTRA_LARGE)
			System.out.println("Good job-you paid attention to the _ .");		
	}
}

enum Size
{
	SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");//构造器(构造枚举常量)
	
	private Size(String abbreviation){this.abbreviation=abbreviation;}
	public String getAbbreviation(){return abbreviation;}
	
	private String abbreviation;
}

         运行结果:



    

     

     

    



猜你喜欢

转载自blog.csdn.net/u012561696/article/details/19558693