这里写目录标题
面向对象
前提
- 要有继承关系
- 要有方法重写
- 要有父类引用子类对象
public class Animal {
public void eat() {
System.out.println("动物吃饭");
}
}
public class Cat extends Animal{
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal{
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Animal animal = new Cat();
animal.eat();
}
}
1. 多态中成员访问特点
1.1 成员变量
- 编译看左边(父类),运行看左边(父类 )
public class Demo2_Polymorphic{
public static void main(String[] args) {
// TODO Auto-generated method stub
Father father = new Son();
System.out.println(father.num);
Son son = new Son();
System.out.println(son.num);
}
}
class Father {
int num = 10;
}
class Son extends Father{
int num = 20;
}
输出
:
10
20
原因:且看内存图
执行Father father = new Son();
father是父类,编译时只能看到 super 区域。
- 执行到
Son son = new Son();
因为 son 是子类,所以它编译时可以看到地址0x0022中的全部内容,因为自己有num值,所以调用 son.num。
1.2 成员方法
- 编译看左边(父类),运行看右边(子类)【这种也叫动态绑定】
public class Demo2_Polymorphic {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father father = new Son();
father.print();
}
}
class Father {
int num = 10;
public void print() {
System.out.println("father");
}
}
class Son extends Father{
int num = 20;
public void print() {
System.out.println("son");
}
}
输出结果:son
原因:且看内存图
f.print()
编译时
f.print()
运行时
1.3 静态成员方法
- 编译看左边(父类),运行看左边(父类 )
- 静态和类相关,算不上重写,所以访问还是左边
所以,只有非静态的成员方法才是编译看左,运行看右
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father father = new Son();
father.method(); //相当于Father.method()
}
}
class Father {
int num = 10;
public void print() {
System.out.println("father");
}
public static void method() {
System.out.println("father static method");
}
}
class Son extends Father{
int num = 20;
public void print() {
System.out.println("son");
}
public static void method() {
System.out.println("son static method");
}
}
输出:father static method
1.4 多态中向上转型和向下转型
- Person p = new SuperMan(); // 向上转型
- SuperMan() sm = (SuperMan)p; //向下转型
1.5 多态的利弊与 instanceof
好处
- 提高代码的维护性(继承保证)
- 提高代码的扩展性(多态保证)
- 可以当作形式参数,可以接收任何子类对象
弊端
- 不能使用子类特有的属性和方法
开发时很少使用父类引用指向子类对象,直接创建子类对象更方便,当作参数时使用多态最好,因为扩展性强。
public class Animal {
public void eat() {
System.out.println("动物吃饭");
}
}
public class Cat extends Animal{
public void eat() {
System.out.println("猫吃鱼");
}
public void catMouse() {
System.out.println("抓老鼠");
}
}
public class Dog extends Animal{
public void eat() {
System.out.println("狗吃肉");
}
public void lookHome() {
System.out.println("看家");
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//method(new Cat());
//method(new Dog());
Animal animal = new Cat();//开发时很少使用父类引用指向子类对象,直接创建子类对象更方便
method(animal);
}
public static void method(Animal a) {//当作参数时使用多态最好,因为扩展性强。
// instanceof 判断前面的引用是否是后边的数据类型
if (a instanceof Cat) {
Cat cat = (Cat)a;
cat.eat();
cat.catMouse();
}
else if(a instanceof Dog){
Dog dog = (Dog)a;
dog.eat();
dog.lookHome();
}
else {
a.eat();
}
}
}
1.6 面试题(有助理解)
多态调用成员方法:编译看左边,运行看右边。
public class demo1 {
public static void main(String[] args) {
A a=new B();
a.show();
B b=new C();
b.show();
}
}
class A{
public void show(){
show2();
}
public void show2(){
System.out.println("我");
}
}
class B extends A{
/*public void show(){
show2();
}*/
public void show2(){
System.out.println("爱");
}
}
class C extends B{
public void show(){
super.show();
}
public void show2(){
System.out.println("你");
}
}
输出:
爱
你
解析:
A a=new B();
a.show();
首先,编译看左边,A类中有show()方法,所以不会报错,编译通过;运行看右边,B类没有show()方法,但是因为他是 A 的子类,父类中有show()方法,且调用的show2()方法。【所以你就当作他有show()方法,且调用show2()】
B b=new C();
b.show();
首先,编译看左边,B类中没有show()方法,但是因为他是 A 的子类,父类中有show()方法,所以编译通过;运行看右边, 调用super.show()
我们可以把A中的show()当作是B中的show(),且调用show2()
。C类有show2()
方法,所以直接运行。
2. 抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
特点:
- 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者接口
- 抽象类不能被实例化
- 抽象类的子类要么是抽象类,要么重写父类所有的抽象方法。
注意
:在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2.1 抽象类成员特点
- 成员变量:可以是变量,也可以是
final常量
,abstract不能修饰成员变量。 - 构造方法:有
- 成员方法
- 抽象方法:强制要求子类必须做的事,所以子类必须重写
- 非抽象类:子类继承的事情,提高代码复用率
public abstract class Animal {
private String name;
private int age;
public Animal() {}//空参
public Animal(String name,int age) {//有参
this.name = name;
this.age = age;
}
public abstract void eat();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Dog extends Animal{
public Dog() {}
public Dog(String name,int age) {
super(name,age);
}
public void eat() {
System.out.println("狗吃肉");
}
public void lookHome() {
System.out.println("看家");
}
}
public class Cat extends Animal{
public Cat() {}
public Cat(String name,int age) {
super(name,age);
}
public void eat() {
System.out.println("猫吃鱼");
}
public void catMouse() {
System.out.println("抓老鼠");
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Cat cat = new Cat("加菲",8);
System.out.println(cat.getName()+"........"+cat.getAge());
cat.eat();
cat.catMouse();
Dog dog = new Dog();
System.out.println(dog.getName()+"........"+dog.getAge());
dog.eat();
dog.lookHome();
}
}
加菲........8
猫吃鱼
抓老鼠
null........0
狗吃肉
看家
2.2 抽象类面试题
2.3 总结
-
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
-
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
抽象类中的
抽象方法只是声明,不包含方法体
,就是不给出方法的具体实现也就是方法的具体功能。 -
构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法
。 -
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
3. 接口
概念:
- 从狭义的角度讲就是指 java 中的 interface
- 从广义的角度讲对外提供规则的都是接口。
特点
- 接口用关键字interface表示
- interface 接口名{}
接口中方法都是抽象的
- 类实现接口用implements表示
- class类名implements 接口名{}
- 接口不能实例化
- 那么,接口如何实例化呢?
- 按照多态的方式来实例化。
- 接口的子类
- 可以是抽象类。但是意义不大。
- 可以是具体类。要重写接口中的所有抽象方法。(推荐方案)
3.1 接口的成员特点
成员变量;只能是常量,并且是静态的并公共的。
- 默认修饰符: public static final
- 建议:自己手动给出。
构造方法:
- 接口没有构造方法。
成员方法:只能是抽象方法
。
- 默认修饰符: public abstract
- 建议:自己手动给出。