1)语法:使用private关键字,修饰字段和方法。只能在当前类中使用。
2)意义 :安全、对类的谁用皇城呢个本降低了。
1)什么是继承:就是将共性进行抽取,使用关键字extends来实现继承。
设计一个类表示动物
// Animal.java
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
// Cat.java
class Cat {
public String name;
public Cat(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
class Bird {
public String name;
public Bird(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
- 这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的.
- 这三个类都具备一个相同的 name 属性, 而且意义是完全一样的.
- 从逻辑上讲, Cat 和 Bird 都是一种 Animal (is - a 语义).
此时我们就可以让 Cat 和 Bird 分别继承 Animal 类, 来达到代码重用的效果.
2)优点:代码可以进行重用。
3)关键字super:
super表示对父类对象的应用
1.super.data //访问父类的成员变量
2.super.func() //访问父类的成员方法
3.super() //访问父类的成员构造方法
对于this和super的区别请看这里: Java中super和this的详解
4)关键字protected
详情请看这里: 关键字protected详解
tips:
在Java中,只能通过extends来继承一个类,不能继承多个类。【单继承】
想详细了解Java继承请戳这里: Java继承详解
3、组合和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。
例如表示一个学校
public class Student {
...
}
public class Teacher {
...
}
public class School {
public Student[] students;
public Teacher[] teachers;
}
组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段
组合表示 has - a 语义
在刚才的例子中, 我们可以理解成一个学校中 “包含” 若干学生和教师.
继承表示 is - a 语义
在上面的 “动物和猫” 的例子中, 我们可以理解成一只猫也 “是” 一种动物
多态是同一个行为具有多个不同表现形式或形态的能力。
在了解多态前需要先了解几个概念:重写、向上转型、向下转型、动态绑定
1)重写(Override) ······ //搬运重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){ //外壳不变
System.out.println("狗可以跑和走"); //核心重写
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
}
}
以上实例编译运行结果如下:
动物可以移动
狗可以跑和走
在上面的例子中可以看到,尽管 b 属于 Animal 类型,但是它运行的是 Dog 类的 move方法。
这是由于在编译阶段,只是检查参数的引用类型。
然而在运行时,Java 虚拟机(JVM)指定对象的类型并且运行该对象的方法。
因此在上面的例子中,之所以能编译成功,是因为 Animal 类中存在 move 方法,然而运行时,运行的是特定对象的方法。
思考以下例子:
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
public void bark(){
System.out.println("狗可以吠叫");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
b.bark();
}
}
以上实例编译运行结果如下:
TestDog.java:30: cannot find symbol
symbol : method bark()
location: class Animal
b.bark();
^
该程序将抛出一个编译错误,因为b的引用类型Animal没有bark方法。
方法的重写规则-
参数列表与被重写方法的参数列表必须完全相同。
-
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
-
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
-
父类的成员方法只能被它的子类重写。
-
声明为 final 的方法不能被重写。
-
声明为 static 的方法不能被重写,但是能够被再次声明。
-
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
-
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
-
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
-
构造方法不能被重写。
-
如果不能继承一个类,则不能重写该类的方法。
当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
super.move(); // 应用super类的方法
System.out.println("狗可以跑和走");
}
}
public class TestDog{
public static void main(String args[]){
Animal b = new Dog(); // Dog 对象
b.move(); //执行 Dog类的方法
}
}
以上实例编译运行结果如下:
重载(Overload)动物可以移动
狗可以跑和走
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
//以下两个参数类型顺序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
重写与重载之间的区别
总结
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
来看代码:
class Animal {
protected String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(name+" eat()");
}
}
class Dog extends Animal{
public Dog(String name,int age) {
super(name,age);
}
public void eat() {
System.out.println("Dog::eat()");
}
}
class Bird extends Animal{
public String selfFiled;
public Bird(String name, int age, String selfFiled) {
super(name, age);
this.selfFiled = selfFiled;
}
public void eat() {
System.out.println("Bird::eat()");
}
public void fly() {
System.out.println(name+"fly()");
}
}
public class TestDemo {
public static void main6(String[] args) {
Animal animal = new Dog("xiaogou",14);
animal.eat();//父类引用 引用子类对象
}
public static void main5(String[] args) {
Animal animal2 = new Bird("小鸟",18,"self");
animal2.eat();
//向下转型
Bird bird = (Bird)animal2;
bird.fly();
System.out.println("=========向下转型不安全========");
Animal animal = new Dog("xiaogou",14);
//向下转型 但是不安全的
if(animal instanceof Bird) {//instanceof表判断
Bird bird2 = (Bird)animal;
bird2.fly();
}
}
}
向下转型注意事项
- 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
- 向下转型只能转型为本类对象(狗不会飞)
用上面的代码来说
当执行main函数时,输出为:
Dog::eat()
两个eat方法,程序为什么会选择Dog类呢,这里我们就引出了运行时绑定。
首先需要知道:运行时绑定绑定的是这个和父类相同的子类的方法。
1…发生向上转型(父类引用 引用子类对象)
2.通过父类引用,调用父类和子类的重名覆盖(重写)方法
那为什么叫运行时绑定?因为在编译的时候,程序绑定的还是Animal的eat方法,只不过在运行的时候最终经绑定到Dog的eat方法
运行时绑定是多态的基础
OK那说了这么多,我们现在再来说多态
class Animal {
protected String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(name+" eat()");
}
}
class Dog extends Animal{
public Dog(String name,int age) {
super(name,age);
}
public void eat() {
System.out.println("Dog::eat()");
}
}
class Bird extends Animal{
public String selfFiled;
public Bird(String name, int age, String selfFiled) {
super(name, age);
this.selfFiled = selfFiled;
}
public void eat() {
System.out.println("Bird::eat()");
}
public void fly() {
System.out.println(name+"fly()");
}
}
public class TestDemo {
public static void func(Animal animal) {
animal.eat();
}
public static void main7(String[] args) {
Dog dog = new Dog("xiaogou",14);
func(dog);
Bird bird = new Bird("小鸟",18,"self");
func(bird);
}
}
执行程序输出
Dog::eat()
Bird::eat()
在这里,通过Animal只是调用一个eat() 方法,因为传过去的参数不一样,所以他表现得形式也不一样,这种表现形式 就叫做多态,因此多态只是一个思想。
1.是通过一个Animal的引用 调用eat方法
2.传递的参数不一样,代表引用的对象不一样
3.通过这个引用,调用同一个方法,表现出了不同的形式,这种思想、这种情况,叫做多态
1.向上转型
2.方法重写
这里又引出了向上转移的三种形式
传参 、直接赋值 和 返回值
有了面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态(polypeptide) 的形式来设计程序了.
我们可以写一些只关注父类的代码, 就能够同时兼容各种子类的情况
代码示例: 打印多种形状
class Shape {
public void draw() {
// 啥都不用干
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("○");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("□");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("♣");
}
}
/我是分割线//
// Test.java
public class Test {
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new Cycle();
Shape shape3 = new Rect();
drawMap(shape1);
drawMap(shape2);
drawMap(shape3);
}
// 打印单个图形
public static void drawShape(Shape shape) {
shape.draw();
}
}
在这个代码中, 分割线上方的代码是类的实现者编写的, 分割线下方的代码是类的调用者编写的。
当类的调用者在编写 drawMap 这个方法的时候, 参数类型为Shape (父类), 此时在该方法内部并不知道, 也不关注当前的shape引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为多态
多态顾名思义, 就是 “一个引用, 能表现出多种不同形态”
如果面试官问什么是多态可以这样回答:父类引用,引用类对象,通过父类引用,调用父类和子类同名的覆盖方法。此时如果父类引用,引用的子类对象不一样,调用这个重写的方法,表现的行为也是不一样的。
使用多态的好处使用多态的好处是什么?
- 类调用者对类的使用成本进一步降低.
封装是让类的调用者不需要知道类的实现细节.
多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.
因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低.
这也贴合了 <<代码大全>> 中关于 “管理代码复杂程度” 的初衷. - 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
- 可扩展能力更强
本文某些部分为搬运和借鉴,所用链接放在这里
Java 重写(Override)与重载(Overload)
Java 中的多态
Java 多态
本文为个人理解,仅供参考!



