3-15 什么是继承?继承机制的作用是什么?子类继承了父类的什么?子类不需要父类的成员时怎么办?能够删除他们吗?java语言允许一个类有多个父类吗?p83
继承是面向对象的核心特性,是实现抽象与共享,构造可复用软件的有效机制,可以最大限度的实现代码复用。
‘子类继承了父类的所有成员变量,包括实例成员变量与静态成员变量;继承了父类除构造方法以外的成员方法,包括实例成员方法和静态成员方法,包括析构方法。
子类可以增加成员,不能删除从父类继承来的成员,但可以重定义他们。
不允许,只能单继承
3-16 子类能够访问父类中什么样权限的成员?p85
子类能访问父类的公有和保护成员,不能访问私有成员,可以访问当前包中父类的缺省成员,不能访问其他包的父类的缺省成员。
3-17 一个类如果没有声明父类,那么他的父类是谁?p85
Object类
部分声明:
public class Object{
public Object();
public String toString();
public boolean equals(Object obj);
protected void finalize() throws Throwable;
}
3-18 声明Object类的作用是什么?Object类中声明了哪些方法?Object类在java类层次体系中的地位是怎样的?Object类没有成员变量,其构造方法Object()有什么作用?
Object类是所有类的父类,声明一个Object类的作用就是可以传递任何类型的类来使用。Object是所有类的父类,它有很多类对象会用到的方法,例如比较常用的toString 、equals,当你新建xx类时,你可以重写Object已经定义的方法,也可以直接调用Object中的方法,如果你写一个封装的方法,不确定传进来的是什么类型的值,就可以使用Object作为一个笼统类。
方法声明 | 功能介绍 |
Object() | 使用无参方式构造对象 |
boolean equals(Object obj) | 用于判断调用对象是否与参数对象相等。 该方法默认比较两个对象的地址是否相等,与 == 运算符的结果一致 若希望比较两个对象的内容,则需要重写该方法。 若该方法被重写后,则应该重写hashCode方法来保证结果的一致 性。 |
int hashCode() | 用于获取调用对象的哈希码值(内存地址的编号)。 若两个对象调用equals方法相等,则各自调用该方法的结果必须相同 若两个调用对象equals方法不相等,则各自调用该方法的结果应该 不相同。 为了使得该方法与equals方法保持一致,需要重写该方法。 |
String toString() | 用于获取调用对象的字符串形式 该方法默认返回的字符串为:包名.类名@哈希码值的十六进制 为了返回更有意义的数据,需要重写该方法 使用print或println打印引用或字符串拼接引用都会自动调用该方法 |
Class getClass() | 用于返回调用对象执行时的Class实例,反射机制使用 |
Object类中只提供一个无参的构造方法,供自己使用和子类的继承. 这也是我们在自定义类时如果没有手动写构造器, 系统默认给出的无参构造的来源.
3-19 如果子类声明的成员与父类成员同名会怎样?p87
1.子类的成员变量与父类同名,则隐藏父类的成员变量,子类的变量类型可能与父类不同
2.子类声明的成员方法与父类同名,若参数列表相同,则覆盖(Override)父类的成员方法,返回值类型必须与父类方法的返回值类型相容,子类方法的访问权限不能小于父类方法的访问权限。
若参数列表不同,则重载(overload)父类的成员方法。
3-20 super引用有什么作用?super引用有几种使用方法?在什么情况下需要使用super?p88
当子类有多态成员时,在子类的实例成员方法中,需要调用父类成员,可用“super引用”
super.成员变量
super.成员方法
3-21 什么是多态性?什么是方法的重载?方法的重载和覆盖有什么区别?
重载(overload)和覆盖(overide)是 Java 多态性的不同表现方式。
重载是在一个类中多态性的一种表现,是指在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型。
在使用重载时,需要注意以下几点:
1)重载是通过不同的方法参数来区分的,例如不同的参数个数、不同的参数类型或不同的参数顺序。
2)不能通过方法的访问权限、返回值类型和抛出的异常类型来进行重载。
3)对于继承来说,如果基类方法的访问权限为 privae,那么就不能在派生类对其重载;如果派生类也定义了一个同名的函数,这只是一个新的方法,不会达到重载的效果。
覆盖是指派生类函数覆盖基类函数。覆盖一个方法并对其重写,以达到不同的作用。
在使用覆盖时需要注意以下几点:
1)派生类中的覆盖方法必须要和基类中被覆盖的方法有相同的函数名和参数。
2)派生类中的覆盖方法的返回值必须和基类中被覆盖白方法的返回值相同。
3)派生类中的覆盖方法所抛出的异常必须和基类(或是其子类)中被覆盖的方法所抛出的异常一致。
4)基类中被覆盖的方法不能为 private 否则其子类只是定义了一个方法,并没有对其覆盖。
重载与覆盖的区别主要有以下几个方面:
1)覆盖是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。
2)覆盖只能由一个方法或只能由一对方法产生关系;重载是多个方法之间的关系。
3)覆盖要求参数列表相同;重载要求参数列表不同。
4)覆盖关系中,调用方法体是根据对象的类型(对象对应存储空间类型)来决定;而重载中关系是根据调用时的实参表和形参表来选择方法体的。
3-22 什么是运行时多态?方法的重载和覆盖分别在什么时候能够确定调用多态方法中的哪一个?
3-23 设 " Object obj = "abc" ; ",下列语句有什么错误?如何改正?
System.out.println(obj.toString()+"长度为"+obj.length());
Object obj="abc"; System.out.println(obj.toString()+"长度为"+obj.length());
这句话有问题,object是所有类的基类,它里面没有length()方法,报下述错误
The method length() is undefined for the type Object。
应该改为:
class Main
{
public static void main(String args[])
{
Object obj="abc";
String str=(String)obj;
System.out.println(obj.toString()+"长度为"+str.length());
}
}
3-24 Object类为什么要声明toString()和equals(Object)方法?Object类中的totring()和equals()等方法是怎样实现的?他们对于子类的作用是什么?子类又如何使用他们?
toString()
作用:打印对象信息
重写前:打印的是包名.类名@地址值
重写后:打印的是对象中的属性值(成员变量的值)
// Object中的源码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
因为在java中Object类是基类,所以每个类都会有toString方法。
System.out.println(Object)实际上就是调用 object的toString方法。
我们用的比较多的就是String类的toString 方法,String类重写了Object的toString方法,用于直接返回String的字符串值。
equals()
作用:比较两个对象
重写前:比较对象的地址
重写后:比较对象中的属性值(成员变量的值)
// Object中的源码:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到是使用= =来进行比较的,= =其实是比较两个对象的的内存地址。所以如果 object1.equals(object2)结果为true,则表示equals1和equals2实际上是引用同一个对象,地址一样。
双等号= =如果作用于基本数据类型,则比较的是值是否相等;如果作用于引用类型,则比较的是变量所指向的对象的地址。
对于非String、Date类型,equals比较的是引用类型的变量所指向的对象的地址。但对于String、Date类型,在其类中重写了equals(),所以String的equals()方法是进行内容比较,而不是单纯的引用比较。
Q:为什么要在我们自己的类中重写equals()?
A:我们常常需要自己创建类,比如我们实现一个Person类,它是继承自Object类的,所以它的equals()方法默认使用的是上面源码里的equals()方法,当我们使用equals()进行比较的时候,比较内存地址。那么有可能出现两个Person对象的参数都相同(比如姓名、年龄、身份证号等,在我们的实际认知中认为这两个人就是一个人,应该返回true),但是由于他们的内存地址是不一样的,所以equals()方法会返回false。那么我们就应该重写equals()方法去比较内容,另外要注意重写的规范。
另外,对比时注意空指针异常的问题,也推荐使用 java.util.Objects中的equals方法,Objects(是Objects,不是Object)是一个JDK7 引入的工具类,比较格式boolean b=Objects.equals(a1,a2),下面的源码中可以看出它是如何避免空指针异常的。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
使用:对象调方法
class person{
int age;
String name;
public person() {
}
public person(int age,String name) {
this.age=age;
this.name=name;
}
@Override
public String toString() {
return "name="+name+",age="+age;
}
@Override
public boolean equals(Object obj) {
//return super.equals(obj);
if(this==obj) {
return true;
}
if(obj==null||(obj.getClass()!=this.getClass())) {
return false;
}
person p3=(person)obj;
return this.age==p3.age&&this.name==p3.name;
}
}
class Main
{
public static void main(String args[])
{
person p=new person(10,"lili");
person p2=new person(10,"lili");
System.out.println(p.toString());
System.out.println(p.equals(p2));
}
}
3-25 person类能否声明以下两个equals()方法?为什么?他们是怎样的关系?在什么情况下被调用?person类是否必须声明以下两个equals()方法?
public boolean equals(Person per)
public boolean equals(Object obj)
能
因为参数列表不同,在person类中他们是重载关系
public boolean equals(Object obj)覆盖了Object类的equals方法
不必须声明
class person{
int age;
String name;
public person() {
}
public person(int age,String name) {
this.age=age;
this.name=name;
}
@Override
public String toString() {
return "name="+name+",age="+age;
}
@Override
public boolean equals(Object obj) {
//return super.equals(obj);
if(this==obj) {
return true;
}
if(obj==null||(obj.getClass()!=this.getClass())) {
return false;
}
person p3=(person)obj;
return this.age==p3.age&&this.name==p3.name;
}
//@Override这里会报错,所以不是覆盖
public boolean equals(person per) {
return false;
}
}
class Main
{
public static void main(String args[])
{
person p=new person(10,"lili");
person p2=new person(10,"lili");
Object o=new person(10,"lili");
System.out.println(p.toString());
System.out.println(p.equals(o));
System.out.println(p.equals(p2));
}
}
3-26 如果某方法与继承来的方法有相同名字,而参数不同,两者是什么关系?
class person{
int age;
String name;
public person() {
}
public person(int age,String name) {
this.age=age;
this.name=name;
}
public person(person p) {
this.name=p.name;
this.age=p.age;
}
public void show() {
System.out.println("age="+age+",name="+name);
}
}
class student extends person{
int grade;
public student() {
}
public student(person p,int grade) {
super(p);
this.grade=grade;
}
@Override
public void show() {
System.out.println("age="+age+",name="+name+",grade="+grade);
}
public void show(int x) {
System.out.println("x="+x);
}
}
class Main
{
public static void main(String args[])
{
person p=new person(10,"lihua");
student t=new student(p,6);
p.show();
t.show();
t.show(10);
}
}
3-27 如果某方法与继承来的方法之间只是返回值不同,会怎样,能运行吗?
不行,因为在调用show()时,不能确定调用的是有返回值的还是无返回值的show()
3-28 什么是抽象类?抽象类中是否必须有抽象方法?抽象类中的方法都是抽象方法吗?抽象类和抽象方法的意义何在?
在 Java 中一个没有方法体的方法应该定义为抽象方法 而类中如果有抽象方法,则必须定义为抽象类
抽象类和抽象方法必须用关键字 abstract 修饰
抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
抽象类不能实例化 -> 不加 {}
抽象类有构造方法,不能实例化,那么构造方法有什么用?
用于子类访问父类数据的初始化
一个类如果没有抽象方法,却定义为了抽象类,有什么用?
为了不让创建对象
3-29 以下ClosedFigure类声明有什么错误?
public abstract class ClosedFigure{
public abstract ClosedFigure();
}
构造方法不能声明为抽象类
3-30 什么是最终类?最终类中的方法都是最终方法吗?最终类的意义何在?
使用关键字final修饰的类是最终类,最终类不能被继承,即不能有子类。
最终类包含的都是最终方法,非最终类也能包含最终方法
3-31 关键字final有几种用法?分别用于声明哪些语法成分?
① 可以用来修饰一个类
② 可以用来修饰一个方法
③ 可以用来修饰一个局部变量
④ 可以用来修饰一个成员变量
1、final关键字用于修饰类
格式如下:
public final class 类名称 {
...
}
作用:使当前这个类不能有任何子类。(“太监类”)
注意:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写
2、final关键字用于修饰成员方法
格式如下:
修饰符 final 返回值类型 方法名称(参数列表) {
方法体
}
作用:当final关键字用来修饰一个方法的时候,这个方法就是最终方法,不能够被覆盖重写
注意:对于类、方法来说,abstract关键字和final关键字不能同时使用,因为作用相互矛盾
3、final关键字用于修饰局部变量
格式如下
// ① 第一种基本数据类型情况下的格式
final 基本数据类型 数据名称 = 值;
// ② 引用数据类型情况下的格式
final 类型 对象名 = new 类名();
//例如:final Student stu = new Student();
作用:当final关键字用于修饰局部变量的时候,这个局部变量就不能更改,“一次赋值,终生不变”
注意:对于 基本类型 来说,不可改变指的是变量当中的数据不可改变,但是对于 引用类型 来说,不可改变的指的是变量当中的地址值不可改变
4、final关键字用来修饰成员变量
对于成员变量来说,如果使用了final关键字修饰,那么这个变量也照样是不可变的
① 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
② 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,只能二选一
③ 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值