java基础知识——继承
2014-03-18 10:08
441 查看
1.java中所有继承都是公有继承 ;
子类扩展超类只需指出不同之处 ,如新添方法或覆盖超类方法; 当覆盖超类方法时,若想使用超类的域,需知:子类不能访问超类的私有域,超类的方法可作为访问接口,使用如super.getSalary()方法。super不是一个对象的引用,只是一个指示表一起调用超类方法的特有关键字。
由于子类的构造器不能访问超类的私有域,必须利用超类的构造器对这部分私有域初始化,通过super实现对超类构造器的调用。
2.this两个用途:1引用隐式参数,2调用该类其它构造器 super两个用途:1调用超类方法 2调用超类构造器
3.现有超类employee,子类manager,下同。
一个对象变量(如e)可以引用多种实际类型的现象叫做多态,运行时能自动选择调用哪个方法的现象叫做动态绑定。如e.getSalary,当e引用employee对象时,调用的是employee中的方法,当e引用manager对象时,调用的是manager中的方法。
java中不需要将方法声明为虚拟方法,动态绑定是默认的处理方式。
4.继承层次:java不支持多继承,具体实现参见接口。
多态:is-a ; Manger boss = new Manager(..); Employee[] staff = new Employee[3]; staff[0] = boss; 这里变量staff[0]与boss引用同一个对象,但编译器将staff[0]看做Employee对象,因此用boss调用Manager方法但是不能用staff[0]
动态绑定:1.编译器查看对象的声明类型和方法名,假设调用x.f(param),且隐式参数x声明为c类的对象。一一列举所有c类中名为f的方法和其超类中访问属性为public且名为f的方法。
2.编译器查看调用方法时提供的参数类型。在名为f的方法中存在一个完全匹配的参数类型,就选该方法。这个过程叫重载解析。允许类型转换。
3.如果是private、static、final方法或构造器,则编译器可以准确知道应该调用哪个方法,称为静态绑定。
4.程序运行中,采用动态绑定调用方法时,虚拟机一定调用与x所引用的对象的实际类型最符合的那个类的方法。是子类,还是超类(覆盖一个方法时,子类方法不能低于超类方法的可见性)
虚拟机与显微每个类创建了一个方法表,列出了所有方法的签名和实际调用的方法。
实例:调用e.Salary()的解析过程——
1.虚拟机提取e的实际类型的方法表 2.虚拟机搜索定义getSalary签名的类,此时虚拟机已经知道应该调用哪个类 3.最后,虚拟机调用方法
5.阻止继承:
final类(final类中所有方法自动地成为final方法)不允许扩展,阻止人们定义该类的子类;类中方法定义为final时,子类不能覆盖这个方法。
6.强制类型转换:Manager boss = (Manager) staff[0]; 进行类型转换的唯一原因是:在暂时忽视对象的实际类型之后,使用对象的全部功能。
编译器允许将一个子类的引用赋值給一个超类变量,但是一个超类的引用赋值給一个子类变量,必须进行类型转换。
注意2点:1.只能在继承层次内进行类型转换 2.在将超类转换为子类前,应使用instanceof进行检查。 3.一般,尽量少用类型转换和instanceOf运算符。
6.抽象类:抽象类中可以有多个抽象方法,建议尽量将通用的方法(无论抽象)在超类(无论抽象)中。类即使不含抽象方法也可以声明为抽象类。
abstract class Person{
...
public abstract String getDescription();
}
抽象方法充当占位角色,具体实现在子类中。扩展抽象类有2种方法:1.子类中定义部分抽象方法或抽象方法也不定义,这时子类也需标记为抽象类;2.定义全部的抽象方法,这时子类就不是抽象类了。
抽象类不能被实例化,一个类声明为abstract类,则不能创建该类对象。
! 但是可以定义一个抽象类的对象变量,但是只能引用非抽象类子类的对象 Person p = new Student("vanco","Politics");通过p.getDescription(),引用student子类对象中的方法。如果省略了person超类中的抽象方法,就不能通过变量p调用getDescription()方法了,编译器只允许调用在类中声明的方法。
7.受保护访问:
将方法或域声明为protected ,则允许子类访问。受保护的域要谨慎使用,因为其他人可以通过派生新类,访问受保护域。
受保护的方法更有实际意义,将方法声明为protected,表明子类得到信任,而其他类不行。
8.Object:所有类的超类
可使用object变量引用任何类型的对象。 java中只有基本类型不是对象(如数值、布尔类型、字符的值),所有的数组类型,不论是对象数组还是基本类型的数组都扩展与Object类。
下面针对几个常用方法,介绍一下:
1.equals方法:检测一个对象是否等于另一个对象,该方法判断对象是否具有相同的引用,但更有意义的是,判断两个对象状态是否相等。下面是equals使用技巧:
class Employee{
public boolean equals(Object otherObject)
{
if(this == otherobject) return true;
if(OtherObject == null) return false;
if(getClass != otherObject.getClass) return false;
Employee other = (Employee)otherObject;
return name.equals(other.name) &&salary == other.salary && hireDay.equals(other.hireDay);
}
}
总结上面,一个完美的equals方法有五步:
1.显式参数命名为otherObject,稍后要转换为另一个叫other 的变量。
2.检测this与otherObject是否引用同一个对象。实际上,这句只是一个优化。
3.检测otherObject是否为null,如果为null,返回false。
4.比较this与otherObject是否是与同一个类。若equals的语义在每个类中有所改变,就是用getClass检测;如果所有的子类都拥有统一的语义,就是用instanceOf检测:
if(!otherObject instanceOf className) return false;
5.将otherObject转化为相应的类类型变量
6.现在对所需要比较的域进行比较。
equals具有下列特性:自反性;对称性;传递性;一致性;对于任意非空引用,x.equals(null)应返回false
若在子类中重新定义equals,就要在其中包含调用super.equals(other)
对数组类型的域,可使用静态的Arrays.equals方法检测相应的数组元素是否相等。
当隐式参数和显式参数不属于同一个类应怎么办:分为2种情况
1.若子类能拥有自己的相等概念,则对称性需求将枪支采用getClass进行检测
2.若超类决定相等的概念,那么就可以使用instanceOf进行检测,这样可以在不同的子类对象之间进行相等的比较。
2.HashCode方法:
字符串的散列码是由内容导出的;而StringBuffer了的散列码是由Object类的默认hashcode方法导出的对象存储地址。
当需要重新定义equals方法,则必须重新定义hashCode方法,以便用户将对象插入到散列表中。
class{
public int hashcode(){
return 7*name.hashCode() + 11*new Double(Salary).hashCode()+13*hireDay.hashCode();
}
..
}
equals 与hashCode的定义必须一致,如果x.equals(y)返回true,那么x.hashCode必须与y.hashCode()具有相同的值。两个相等的对象要有相同的散列码。
若存在数组类型的域,可以使用静态的Arrays.hashCode方法计算一个散列码
3.toString
用于返回表示对象值的字符串,格式为:类名,随后是一对方括号括起来的域。强烈建议为每个自定义的类增加toString方法,是自己和所有使用该类的人受益。
最好通过调用getClass.getName获得类名的字符串,如果已经在超类中定义getClass.getName,子类只需要调用super.toString就好了。
只要对象与一个字符串通过操作符+连接起来,java编译器就会自动的调用toString方法,且打印一个对象x时,println也会直接调动x.toString方法。
不同的是,数组继承了object类中的toString方法,所以按旧方式打印,修正的方式调用静态方法Arrays.toString,多维数组调用Arrays.deepToString
9.泛型数组列表
int actualSize= ... Employee[] Staff = new Employee[actualSize] 允许在运行时确定数组列表,但一旦确定就不好修改大小。解决的最简单方法就是使用ArrayList,可自动调节数组容量,ArrayList<Employee> staff = new ArrayList<Employee>();
使用add方法添加元素 staff.add(new Employee("Harry Hacker",...));数组列表管理着对象引用的一个内部数组,如果调用add且内部数组已经满了,数组列表就将自动创建一个更大的数组,并将所有对象从较小的数组中拷贝到较大的数组中。
调用staff.ensureCapacity(100)将分配一个包含100个对象的内部数组,然后调用100次add,而不用重新分配空间;另外,还可以把初始化容量传递给ArrayList构造器:
ArrayList<Employee> staff = new ArrayList<Employee>(100),与new Employee[100]的区别是——第一个表示拥有保存100个元素的潜力,当重新分配空间的时候,将会超过100个元素,但最初初始化构造后不含任何元素。
staff.size返回数组列表中包含的实际元素数量,等价于数组a的length
一旦确定数组大小不再变化,可以调用trimToSize,将当前存储区域大小调整为当前元素数量所需的存储空间数目。
staff.set(i,harry);设置第i个元素。staff.get(i)获取数组列表的元素。
下面一个技巧可以一举两得,既可以灵活扩展数组,又可以方便访问数组元素:
首先创建一个数组,添加所有元素。
ArrayList<x> list = new ArrayList<x>();
while(...)
{
x = ...;
list.add(x);
}
然后使用toArray方法将数组元素拷贝到一个数组中
x[] a = new X[list.size()];
list.toArray(a);
除了在尾部追加元素,还可以在中间插入元素
int n= staff.size() / 2;
staff.add(n,e);
移除使用Employee e = staff.remove(n);
如果数组存储的元素比较多,又经常需要在中间位置插入删除元素,就应该考虑使用链表了。
10.对象包装器与自动打包:
将int这样的基本类型转换为对象。所有的基本类型都有一个对应的对象。
对象包装器类有:Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean;对象包装器是不可变的,即一旦构造了对象包装器,便不允许更改包装器在其中的值。同时,它们还是final类,因此不能定义他们的子类。
假设想定义一个整型数组列表,而尖括号中参数不允许是基本类型,这时我们可以用:
ArrayList <Integer> list = new ArrayList<Integer>(); 但是ArrayList <Integer>效率远低于int[]数组,因此更应使用它来构造小型集合。
自动打包:list.add(3)会被自动变换为list.add(new Integer(3));
自动拆包:当一个integer对象赋值给一个int值时,会自动拆包,int n = list.get(i)被翻译为 int n = list.get(i).intValue() ;
在算术表达式中也能自动打包拆包。
==运算符也可应用于对象包装器,检测的是对象是否指向同一个区域。
最后,通常我们可以将某些基本方法放置在对象包装器中,如将一个字符串转化为整型 int x = Integer.parseInt(s);这里与Integer没关系,parseInt是一个静态方法,但Integer类是放这个方法的好地方
11.参数数量可变的方法
public Pritnstream printf(String fmt, Object... args){} ...表明这个方法可以接受任意数量的对象。
12.枚举类
声明 public enum size{SMALL,MEDIUM,LAREGE,EXTRA_LARGE}
enum Size
{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
private Size(){ }
}
所有枚举类型都是Enum的子类。
Size.SMALL.toString返回字符串“SMALL” 逆的静态方法valueOf ,Size s = (size)Enum.valueOf(Size.class,"SMALL")将s设置为Size.SMALL
Size[] values = Size.values[] 返回一个包含全部枚举值的数组。
13.继承设计的技巧:
1.将公共 操作和域放在超类
2.不要使用受保护的域
3.使用继承实现is-a
4.除非所有继承的方法都有意义,否则不要使用继承。
5.在覆盖原方法时,不要改变预期的行为。
6.使用多态,而非类型信息。
7.不要过多使用反射。
子类扩展超类只需指出不同之处 ,如新添方法或覆盖超类方法; 当覆盖超类方法时,若想使用超类的域,需知:子类不能访问超类的私有域,超类的方法可作为访问接口,使用如super.getSalary()方法。super不是一个对象的引用,只是一个指示表一起调用超类方法的特有关键字。
由于子类的构造器不能访问超类的私有域,必须利用超类的构造器对这部分私有域初始化,通过super实现对超类构造器的调用。
2.this两个用途:1引用隐式参数,2调用该类其它构造器 super两个用途:1调用超类方法 2调用超类构造器
3.现有超类employee,子类manager,下同。
一个对象变量(如e)可以引用多种实际类型的现象叫做多态,运行时能自动选择调用哪个方法的现象叫做动态绑定。如e.getSalary,当e引用employee对象时,调用的是employee中的方法,当e引用manager对象时,调用的是manager中的方法。
java中不需要将方法声明为虚拟方法,动态绑定是默认的处理方式。
4.继承层次:java不支持多继承,具体实现参见接口。
多态:is-a ; Manger boss = new Manager(..); Employee[] staff = new Employee[3]; staff[0] = boss; 这里变量staff[0]与boss引用同一个对象,但编译器将staff[0]看做Employee对象,因此用boss调用Manager方法但是不能用staff[0]
动态绑定:1.编译器查看对象的声明类型和方法名,假设调用x.f(param),且隐式参数x声明为c类的对象。一一列举所有c类中名为f的方法和其超类中访问属性为public且名为f的方法。
2.编译器查看调用方法时提供的参数类型。在名为f的方法中存在一个完全匹配的参数类型,就选该方法。这个过程叫重载解析。允许类型转换。
3.如果是private、static、final方法或构造器,则编译器可以准确知道应该调用哪个方法,称为静态绑定。
4.程序运行中,采用动态绑定调用方法时,虚拟机一定调用与x所引用的对象的实际类型最符合的那个类的方法。是子类,还是超类(覆盖一个方法时,子类方法不能低于超类方法的可见性)
虚拟机与显微每个类创建了一个方法表,列出了所有方法的签名和实际调用的方法。
实例:调用e.Salary()的解析过程——
1.虚拟机提取e的实际类型的方法表 2.虚拟机搜索定义getSalary签名的类,此时虚拟机已经知道应该调用哪个类 3.最后,虚拟机调用方法
5.阻止继承:
final类(final类中所有方法自动地成为final方法)不允许扩展,阻止人们定义该类的子类;类中方法定义为final时,子类不能覆盖这个方法。
6.强制类型转换:Manager boss = (Manager) staff[0]; 进行类型转换的唯一原因是:在暂时忽视对象的实际类型之后,使用对象的全部功能。
编译器允许将一个子类的引用赋值給一个超类变量,但是一个超类的引用赋值給一个子类变量,必须进行类型转换。
注意2点:1.只能在继承层次内进行类型转换 2.在将超类转换为子类前,应使用instanceof进行检查。 3.一般,尽量少用类型转换和instanceOf运算符。
6.抽象类:抽象类中可以有多个抽象方法,建议尽量将通用的方法(无论抽象)在超类(无论抽象)中。类即使不含抽象方法也可以声明为抽象类。
abstract class Person{
...
public abstract String getDescription();
}
抽象方法充当占位角色,具体实现在子类中。扩展抽象类有2种方法:1.子类中定义部分抽象方法或抽象方法也不定义,这时子类也需标记为抽象类;2.定义全部的抽象方法,这时子类就不是抽象类了。
抽象类不能被实例化,一个类声明为abstract类,则不能创建该类对象。
! 但是可以定义一个抽象类的对象变量,但是只能引用非抽象类子类的对象 Person p = new Student("vanco","Politics");通过p.getDescription(),引用student子类对象中的方法。如果省略了person超类中的抽象方法,就不能通过变量p调用getDescription()方法了,编译器只允许调用在类中声明的方法。
7.受保护访问:
将方法或域声明为protected ,则允许子类访问。受保护的域要谨慎使用,因为其他人可以通过派生新类,访问受保护域。
受保护的方法更有实际意义,将方法声明为protected,表明子类得到信任,而其他类不行。
8.Object:所有类的超类
可使用object变量引用任何类型的对象。 java中只有基本类型不是对象(如数值、布尔类型、字符的值),所有的数组类型,不论是对象数组还是基本类型的数组都扩展与Object类。
下面针对几个常用方法,介绍一下:
1.equals方法:检测一个对象是否等于另一个对象,该方法判断对象是否具有相同的引用,但更有意义的是,判断两个对象状态是否相等。下面是equals使用技巧:
class Employee{
public boolean equals(Object otherObject)
{
if(this == otherobject) return true;
if(OtherObject == null) return false;
if(getClass != otherObject.getClass) return false;
Employee other = (Employee)otherObject;
return name.equals(other.name) &&salary == other.salary && hireDay.equals(other.hireDay);
}
}
总结上面,一个完美的equals方法有五步:
1.显式参数命名为otherObject,稍后要转换为另一个叫other 的变量。
2.检测this与otherObject是否引用同一个对象。实际上,这句只是一个优化。
3.检测otherObject是否为null,如果为null,返回false。
4.比较this与otherObject是否是与同一个类。若equals的语义在每个类中有所改变,就是用getClass检测;如果所有的子类都拥有统一的语义,就是用instanceOf检测:
if(!otherObject instanceOf className) return false;
5.将otherObject转化为相应的类类型变量
6.现在对所需要比较的域进行比较。
equals具有下列特性:自反性;对称性;传递性;一致性;对于任意非空引用,x.equals(null)应返回false
若在子类中重新定义equals,就要在其中包含调用super.equals(other)
对数组类型的域,可使用静态的Arrays.equals方法检测相应的数组元素是否相等。
当隐式参数和显式参数不属于同一个类应怎么办:分为2种情况
1.若子类能拥有自己的相等概念,则对称性需求将枪支采用getClass进行检测
2.若超类决定相等的概念,那么就可以使用instanceOf进行检测,这样可以在不同的子类对象之间进行相等的比较。
2.HashCode方法:
字符串的散列码是由内容导出的;而StringBuffer了的散列码是由Object类的默认hashcode方法导出的对象存储地址。
当需要重新定义equals方法,则必须重新定义hashCode方法,以便用户将对象插入到散列表中。
class{
public int hashcode(){
return 7*name.hashCode() + 11*new Double(Salary).hashCode()+13*hireDay.hashCode();
}
..
}
equals 与hashCode的定义必须一致,如果x.equals(y)返回true,那么x.hashCode必须与y.hashCode()具有相同的值。两个相等的对象要有相同的散列码。
若存在数组类型的域,可以使用静态的Arrays.hashCode方法计算一个散列码
3.toString
用于返回表示对象值的字符串,格式为:类名,随后是一对方括号括起来的域。强烈建议为每个自定义的类增加toString方法,是自己和所有使用该类的人受益。
最好通过调用getClass.getName获得类名的字符串,如果已经在超类中定义getClass.getName,子类只需要调用super.toString就好了。
只要对象与一个字符串通过操作符+连接起来,java编译器就会自动的调用toString方法,且打印一个对象x时,println也会直接调动x.toString方法。
不同的是,数组继承了object类中的toString方法,所以按旧方式打印,修正的方式调用静态方法Arrays.toString,多维数组调用Arrays.deepToString
9.泛型数组列表
int actualSize= ... Employee[] Staff = new Employee[actualSize] 允许在运行时确定数组列表,但一旦确定就不好修改大小。解决的最简单方法就是使用ArrayList,可自动调节数组容量,ArrayList<Employee> staff = new ArrayList<Employee>();
使用add方法添加元素 staff.add(new Employee("Harry Hacker",...));数组列表管理着对象引用的一个内部数组,如果调用add且内部数组已经满了,数组列表就将自动创建一个更大的数组,并将所有对象从较小的数组中拷贝到较大的数组中。
调用staff.ensureCapacity(100)将分配一个包含100个对象的内部数组,然后调用100次add,而不用重新分配空间;另外,还可以把初始化容量传递给ArrayList构造器:
ArrayList<Employee> staff = new ArrayList<Employee>(100),与new Employee[100]的区别是——第一个表示拥有保存100个元素的潜力,当重新分配空间的时候,将会超过100个元素,但最初初始化构造后不含任何元素。
staff.size返回数组列表中包含的实际元素数量,等价于数组a的length
一旦确定数组大小不再变化,可以调用trimToSize,将当前存储区域大小调整为当前元素数量所需的存储空间数目。
staff.set(i,harry);设置第i个元素。staff.get(i)获取数组列表的元素。
下面一个技巧可以一举两得,既可以灵活扩展数组,又可以方便访问数组元素:
首先创建一个数组,添加所有元素。
ArrayList<x> list = new ArrayList<x>();
while(...)
{
x = ...;
list.add(x);
}
然后使用toArray方法将数组元素拷贝到一个数组中
x[] a = new X[list.size()];
list.toArray(a);
除了在尾部追加元素,还可以在中间插入元素
int n= staff.size() / 2;
staff.add(n,e);
移除使用Employee e = staff.remove(n);
如果数组存储的元素比较多,又经常需要在中间位置插入删除元素,就应该考虑使用链表了。
10.对象包装器与自动打包:
将int这样的基本类型转换为对象。所有的基本类型都有一个对应的对象。
对象包装器类有:Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean;对象包装器是不可变的,即一旦构造了对象包装器,便不允许更改包装器在其中的值。同时,它们还是final类,因此不能定义他们的子类。
假设想定义一个整型数组列表,而尖括号中参数不允许是基本类型,这时我们可以用:
ArrayList <Integer> list = new ArrayList<Integer>(); 但是ArrayList <Integer>效率远低于int[]数组,因此更应使用它来构造小型集合。
自动打包:list.add(3)会被自动变换为list.add(new Integer(3));
自动拆包:当一个integer对象赋值给一个int值时,会自动拆包,int n = list.get(i)被翻译为 int n = list.get(i).intValue() ;
在算术表达式中也能自动打包拆包。
==运算符也可应用于对象包装器,检测的是对象是否指向同一个区域。
最后,通常我们可以将某些基本方法放置在对象包装器中,如将一个字符串转化为整型 int x = Integer.parseInt(s);这里与Integer没关系,parseInt是一个静态方法,但Integer类是放这个方法的好地方
11.参数数量可变的方法
public Pritnstream printf(String fmt, Object... args){} ...表明这个方法可以接受任意数量的对象。
12.枚举类
声明 public enum size{SMALL,MEDIUM,LAREGE,EXTRA_LARGE}
enum Size
{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
private Size(){ }
}
所有枚举类型都是Enum的子类。
Size.SMALL.toString返回字符串“SMALL” 逆的静态方法valueOf ,Size s = (size)Enum.valueOf(Size.class,"SMALL")将s设置为Size.SMALL
Size[] values = Size.values[] 返回一个包含全部枚举值的数组。
13.继承设计的技巧:
1.将公共 操作和域放在超类
2.不要使用受保护的域
3.使用继承实现is-a
4.除非所有继承的方法都有意义,否则不要使用继承。
5.在覆盖原方法时,不要改变预期的行为。
6.使用多态,而非类型信息。
7.不要过多使用反射。
相关文章推荐
- java基础知识之继承
- java内接口,抽象类,继承实现的一些基础知识和实例
- java基础知识6-类的继承以及字段修饰
- Java培训实战教程之Java基础知识精华部分(六)-继承
- java基础知识7-类的继承2
- java基础(2)-基础类型和语法(static、内部类、final、抽象类、接口、封装)(并将这些基础知识与java的三大特征关联(继承、多态、封装))
- JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程...]
- JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程...]
- java基础知识--类和对象、继承(一)
- Java基础知识-面向对象-继承
- java基础第六天——继承以及抽象类相关的知识
- 代码块、继承、this、super、final(java基础知识八)
- Java基础知识之继承
- JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程...]
- 【Java学习笔记】基础知识学习10【继承与多态】
- java基础知识——类的继承
- Java核心技术卷I:基础知识(原书第8版):5.2.2 相等测试与继承
- Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别
- Java基础知识——继承和多态
- JAVA基础知识:面对对象特性【继承】【 多态】【 封装】【 接口】