对象、类和抽象类的区别
对象是一个具体的事物,类是对具有相同属性和行为的一组对象的抽象,对象是类的一个一个人的具体实例;抽象类是一种特殊的类,是对类的进一步抽象,抽象类不能被实例化。
类、抽象类和接口的异同
部分内容借鉴以下博文:http://www.cnblogs.com/qiuxiaoju/p/3174068.html
在面试可能经常被问到,接口有什么作用?相对于类而言,为何需要用类实现接口,接口和抽象类的区别等等诸如此类的问题。如果不仔细思考和总结一下这知识,还真不知道怎么回答。
Java接口和Java抽象类有太多相似的地方,又有太多特别的地方,究竟在什么地方,才是它们的最佳位置呢?把它们比较一下,你就可以发现了。
相同点:
1.都是抽象类型,抽象类和接口都是对类进行抽象;
2.其中的抽象方法都必须被具体类实现;
3.都不可以被实例化,如果要非要通过new创建他们的实例,都需要通过匿名内部类的方式;
4.都可以定义公共的(public)和static属性和方法;如:
public int id = 5;
public final String name = "interface";
public static int age = 22;
public static final int weight = 55;
int i = 0;
不同点:
定义的角度:
1.定义抽象类用abstract class,定义接口用interface;
继承的角度:
2.一个具体的实现类只能继承一个父类,但可以实现多个接口;
属性和方法的角度:
3.抽象类可以定义private和protected属性和方法,而接口只能定义public和default(即默认的没有设定访问权限)的属性和方法;
4.Java抽象类可以提供某些方法的部分实现,而Java接口不可以;
5.抽象类可以有抽象方法和非抽象方法,子类可以有选择的实现抽象类中的非抽象方法,而实现类必须实现接口中的所有方法;
构造方法:
6.接口不可以有构造方法,抽象类可以有构造方法;
设计理念不同:
7.其实子类和抽象类的的关系表示的是”is-a”关系(塑料碗和瓷碗都是碗),是一种律属关系,子类属于父类,因此一个类只能继承继承一个类;子类和接口的关系表示的是“has-a”关系(智能手机继承自手机,同时实现了闹钟、MP3等各种功能),实现类拥有这个接口的特征也可以拥有另外一个借口的特征,因此一个类可以实现多个接口。
概况地说,接口是模块之间的协议,一些模块用于实现某些协议的具体动作,另外一些模块只针对接口编程,这样实现低耦合。
抽象类主要是用于继承设计的,主要用于模板设计!
经典的设计模式:缺省/默认适配模式(Default Adapter)
声明类型的工作仍然由Java接口承担,但是同时给出一个Java抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类。哈,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模式”。
在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。
继承
1、方法(函数)重写(overwrite)要注意的机几点:
1),重写方法(函数)必须和被重写方法具有相同的方法名称、参数列表和返回类型(也可以是原函数返回类型的子类型);
2),重写的时候要从父类的函数声明中拷贝函数的声明(即函数名设参数的类型和个数)。
3),重写方法不能使用比被重写方法跟严格的访问权限。(为避免名称出错,重写时必须从基类中copy函数的声明)
2、super关键字指向当前类的父类。
3、构造方法:
a,子类的构造的过程必须调用其基类的构造方法;
b,子类可以在自己的构造方法中使用super(argument_list参数列表)调用基类的构造方法;即使用this(argument_list_)调用本类的另外的构造方法,如果调用super,必须写在子类构造方法的第一行。
c,如果子类的构造方法中没有显示地调用基类的构造方法,则系统默认调用基类无参数的构造方法;
d,如果子类的构造方法中没有显示地调用基类的构造方法,而基类中又没有无参数的构造方法,则编译出错。
4、Object类是java所有类的根基类,如果在类的声明中未使用extends指明其基类,则默认为object类。
5、对象转型:
a,一个基类的引用类型对象可以“指向”其子类的对象;
b,一个基类的引用的不可以访问子类对象新增加的成员(属性和方法);
c,可以使用引用变量instanceof类名来判断该引用变量所“指向”的对象该类或该类的子类;
d,子类的对象可以当作基类的对象来使用称作向上转型(upcasting),反之称为向下转型(downcasting)。
6、动态绑定是指在“执行”期间(而非在编译期间)判定所引用对象的实际类型,根据实际的类型调用相应的方法。
要符合的条件:a,要有继承;b,要有重写;c,父类引用指向子类对象。
7、抽象类;
a,用abstract关键字来修饰一个类时,这个类叫做抽象类;用abstract来修饰一个方法时,这个方法叫做抽象方法;
b,含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被重写;
c,抽象类不能被实例化;
d,抽象方法只需声明,而不需实现。
8、final关键字(类似于C++中的const)
a,final的变量的值不能被改变;1),final的成员变量; 2)final的局部变量;
b,final的方法不能被重写;
c,final的类不能被继承。
继承和组合的关系
上次阿里面试的时候问到继承和组合的关,什么时候要用组合,什么时候要用继承。真后悔当初没有准备,不然就可以很容易地回答,现在想起来,当时回答的真是烂的不能再烂!
继承和组合的关系主要是要从理念上进行区分,继承是一种从属关系,是子由你来的一种关系;而组合是一种包含关系,是整体与部分的关系。
假设有两个类A和B,当A和B和关系是B is A,用继承:B extends A;当A和B和关系是B has A时,用组合,即B中含有A的实例化对象。
二. 重载方法和重写方法
多态性不会决定调用哪个重载版本方法,它只在决定调用方法的哪个重写版本时才起作用。
重载方法和重写方法的区别:
注:方法重写能实现的前提是在子类中能访问到父类的方法,如果能不能访问到父类的方法,那在子类中定义同名的方法是可以的,此时完全不用按重写方法的那一套约束来。若是子类能访问到父类的方法,那在子类中定义同名的方法则需要按照方法重写的约束来,否则就编译不通过。
重载方法 | 重写方法 | |
变元 | 必须改变 | 一定不能改变 |
返回类型 | 可以改变 | 不能改变(有些地方说协变式可以) |
异常 | 可以改变 | 可以减小或消除。一定不能抛出新的或者更广的检验异常 |
访问级别 | 可以改变 | 一定不能执行更严格的限制(可以降低限制) |
调用 | 引用的声明类型决定了选择哪个重载版本。在编译时刻做出决定。调用的实际方法仍然是一个在运行时发生的虚拟方法调用,但是编译器总是知道所调用方法的签名。因此在运行时,不仅是方法所在的类,而且变元匹配也已经明确了。 | 对象类型(也就是堆上实际的实例的类型)决定调用哪个方法。在运行时做出决定 |
编译器允许的方法调用只基于对象引用声明的类型,而不考虑引用实际的类型。 即对象引用能够引用的方法是由其声明类型决定的,不能引用一个该类型中没有的方法,即使在其子类型中有这个方法。
当引用变量的声明类型(编译时类型)和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明变量的类型决定。但通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象来决定。
例子:
class Base1
{
int count = 2;
public void display()
{
System.out.println(this.count);
}
}
class Derived1 extends Base1
{
int count = 20;
@Override
public void display()
{
System.out.println(this.count);
}
}
public class FieldAndMethodTest
{
public static void main(String[] args)
{
// 声明、创建一个Base对象
Base1 b = new Base1(); //①
// 直接访问count实例变量和通过display访问count实例变量
System.out.println(b.count);
b.display();
// 声明、并创建一个Derived对象
Derived1 d = new Derived1(); //②
// 直接访问count实例变量和通过display访问count实例变量
System.out.println(d.count);
d.display();
// 声明一个Base变量,并将Derived对象赋给该变量
Base1 bd = new Derived1(); //③
// 直接访问count实例变量和通过display访问count实例变量
System.out.println(bd.count);
bd.display();
// 让d2b变量指向原d变量所指向的Dervied对象
Base1 d2b = d; //④
// 访问d2b所指对象的count实例变量
System.out.println(d2b.count);
}
}
结果为:
2
2
20
20
2
20
2
三.继承方法和属性
public class Super {
private int i = 10;
public void execute() {
System.out.println(i);
}
public static void main(String[] args) {
SubClassA subClass = new SubClassA();
Super superClass = subClass;
System.out.println("subClassA value:");
subClass.execute();
System.out.println("super value:");
superClass.execute();
SubClassB subClassB = new SubClassB();
superClass = subClassB;
System.out.println("subClassB value:");
subClassB.execute();
System.out.println("super value:");
superClass.execute();
}
}
class SubClassA extends Super {
private int i = 20;
}
class SubClassB extends Super {
private int i = 30;
public void execute() {
System.out.println(i);
}
}
输出结果是什么呢??
ava继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
也就是说继承主要是为了功能 其次是为了数据,那么我们分析上述答案
1.首先看实际执行的方法是哪个类的?
2.那么执行的方法所在类也就是this访问的变量自然就是哪个类的变量
子类继承父类的方法,子类对象在调用这个方法的时候采取的是一种以声明类型为父类类型,而运行时类型为子类类型的机制。(自己结论)
答案:
subClassA value:
10
super value:
10
subClassB value:
30
super value:
30
如果修改private为public是否答案等同?必须等同