Java面向对象|封装、继承和多态

一.什么是面向对象

  • Java的编程语言是面向对象的,采用这种语言进行编程称为面向对象编程(Object-Oriented Programming, OOP)。

  • 面向对象的方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想。总体来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象。

  • 面向对象的三个基本特征:封装、继承和多态。

二.抽象(Abstract)

  • 忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用关注细节。
  • 比如:需要设计一个学生体检的系统,我们只需要关注学生的身高、体重和肺活量等信息,而不需要关注学生的兴趣爱好等信息。所以说,抽象就是将多个物体的共同点归纳出来,就是抽出像的部分!

三.封装(Encapsulation)

  • 封装是把过程和数据包围起来,对数据的访问只能通过指定的方式。也就是说把复杂的内部细节全部封装起来,只暴露简单的接口。

  • 我们程序设计要追求“高内聚,低耦合”。

    • 高内聚就是类的内部数据操作细节自己完成,不允许外部干涉。
    • 低耦合:仅暴露少量的方法给外部使用。

1.封装的步骤

  • 修改属性的可见性来限制对属性的访问(一般限制为private)。
public class Person {
    private String name;
    private int age;
}
  • 提供一个公开的方法设置或者访问私有的属性 。
    • 设置 通过set方法,命名格式: set属性名(); 属性的首字母要大写 。
    • 访问 通过get方法,命名格式: get属性名(); 属性的首字母要大写。
public class Person{
    private String name;
    private int age;
    public int getAge(){
      return age;
    }
    public String getName(){
      return name;
    }
    public void setAge(int age){
      this.age = age;
    }
    public void setName(String name){
      this.name = name;
    }
}

2.作用和意义

  • 提高程序的安全性,保护数据 。
  • 隐藏代码的实现细节。
  • 统一用户的调用接口。
  • 提高系统的可维护性。
  • 便于调用者调用。

3.方法的重载

  • 方法重载必须满足的条件

    • 方法名必须相同。
    • 参数列表必须不同(参数的类型、个数、顺序的不同)。
    • 方法的返回值可以不同,也可以相同。
    • 修饰符可以不同,也可以相同。
  • Java中,判断一个类中的俩个方法是否相同,主要参考俩个方面:方法名字和参数列表

//参数类型不同
public void test(Strig str){}
public void test(int a){}
//参数个数不同
public void test(Strig str,double d){}
public void test(Strig str){}
//参数顺序不同
public void test(Strig str,double d){}
public void test(double d,Strig str){}

四.继承

1.继承的概述

  • 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
  • 继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
  • 子类中继承了父类中的属性和方法后,在子类中能不能直接使用这些属性和方法,是和这些属性和方法原有的修饰符(public protected default private)相关的。
    • 父类中的属性和方法使用public修饰,在子类中继承后"可以直接"使用。
    • 父类中的属性和方法使用private修饰,在子类中继承后"不可以直接"使用。

2.继承的格式

class 父类 {
}
 
class 子类 extends 父类 {
}

3.extends关键字

  • 在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
public class Animal { 
    private String name;   
    private int id; 
    public Animal(String myName, String myid) { 
        //初始化属性值
    } 
    public void eat() {  //吃东西方法的具体实现  } 
    public void sleep() { //睡觉方法的具体实现  } 
} 
 
public class Dog extends Animal{ 
}

4.implements关键字

  • implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口.
public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

5.super 与 this 关键字

  • 如果不显示调用父类有参构造函数,系统会默认调用父类无参构造函数super();
public class Animal {
    private String name;
    public Animal() {
        System.out.println("Animal创建了");
    }
}

public class Cat extends Animal {
    public Cat() {
        //super();
        System.out.println("Cat创建了");
    }
}

public class Test {
    public static void main(String[] args) {
        Cat cat=new Cat();
    }
}
//输出结果:
//Animal创建了
//Cat创建了
  • 不管是显式还是隐式的父类的构造器,super语句一定要出现在子类构造器中第一行代码。
public class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
        System.out.println("super语句要在第一行");
    }
}
  • super只能出现在子类的方法或者构造方法中。
public class Animal {
    private String name;

    public Animal(){
		System.out.println("Animal无参");
    }

    public Animal(String name) {
        this.name = name;
        System.out.println("小狗:"+name);
    }
}

public class Cat extends Animal {
    private String color;
    public Cat(){
        this("黄色");
    }

    public Cat(String color) {
        super("柯基");
        this.color=color;
        System.out.println("颜色:"+color);
    }

}

public class Test {
    public static void main(String[] args) {
        Cat cat=new Cat();
        //这里因为父类显示调用有参构造,所以系统不会默认调用无参构造,也就不会输出"Animal无参"。
    }
}
//输出结果:
//
  • super与this的区别
    • this:代表所属方法的调用者对象;super:代表父类对象的引用空间。
    • this:在非继承的条件下也可以使用;super:只能在继承的条件下才能使用。
    • this:调用本类的构造方法;super:调用的父类的构造方法。

5.final关键字

  • final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。
//声明类
final class 类名 {//类体}
//声明方法
修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}

6.重写

  • 方法重写只存在于子类和父类(包括直接父类和间接父类)之间。在同一个类中方法只能被重载,不能被重写。
  • 静态方法不能重写。
    • 父类的静态方法不能被子类重写为非静态方法。//编译出错
    • 父类的非静态方法不能被子类重写为静态方法。//编译出错
    • 子类可以定义与父类的静态方法同名的静态方法。(但是这个不是覆盖)
public class Person {
    private String name;
    public static void show(){
        System.out.println("我是人");
    }
}

public class Student extends Person{
    public static void show(){
        System.out.println("我是学生");
    }
}
//可以看出静态方法的调用只和变量声明的类型相关,这个和非静态方法的重写之后的效果完全不同
public class Test {
    public static void main(String[] args) {
        Person person=new Student();
        Student student=new Student();
        person.show();
        student.show();
    }
}
//输出结果:
//我是人
//我是学生
  • 私有方法不能被子类重写,子类继承父类后,是不能直接访问父类中的私有方法的,那么就更谈不上重写了。
public class Person{
	private void run(){}
}
//编译通过,但这不是重写,只是俩个类中分别有自己的私有方法
public class Student extends Person{
	private void run(){}
}
  • 重写的语法
    • 方法名必须相同
    • 参数列表必须相同
    • 访问控制修饰符可以被扩大,但是不能被缩小: public protected default private
    • 抛出异常类型的范围可以被缩小,但是不能被扩大: ClassNotFoundException —> Exception
    • 返回类型可以相同,也可以不同,如果不同的话,子类重写后的方法返回类型必须是父类方法返回类型的子类型。比如,父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型。
public class Person {
    private String name;
    public void show(){
        System.out.println("我是人");
    }
}

public class Student extends Person{
    public static void show(){
        System.out.println("我是学生");
    }
}
//可以看出静态方法的调用只和变量声明的类型相关,这个和非静态方法的重写之后的效果完全不同
public class Test {
    public void main(String[] args) {
        Person person=new Student();
        Student student=new Student();
        person.show();
        student.show();
    }
}
//输出结果:
//我是学生
//我是学生

五.多态

1.多态的概述

  • 多态是同一个行为具有多个不同表现形式或形态的能力。
  • 多态就是同一个接口,使用不同的实例而执行不同操作。

2.多态存在的三个条件

  • ①继承 ②重写 ③父类引用子类对象
  • 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

在这里插入图片描述

class Shape {
    void draw() {}
}
 
class Circle extends Shape {
    void draw() {
        System.out.println("Circle.draw()");
    }
}
 
class Square extends Shape {
    void draw() {
        System.out.println("Square.draw()");
    }
}
 
class Triangle extends Shape {
    void draw() {
        System.out.println("Triangle.draw()");
    }
}

3.重写、重载和多态的关系

  • 重载是编译时多态。调用重载的方法,在编译期间就要确定调用的方法是谁,如果不能确定则编译报错。
  • 重写是运行时多态。调用重写的方法,在运行期间才能确定这个方法到底是哪个对象中的。这个取决于调用方法的引用,在运行期间所指向的对象是谁,这个引用指向哪个对象那么调用的就是哪个对象中的方法。(java中的方法调用, 是运行时动态和对象绑定的)

4.无法表现多态特性的三种情况

  • static方法,因为被static修饰的方法是属于类的,而不是属于实例的
  • final方法,因为被final修饰的方法无法被子类重写
  • private方法和protected方法,前者是因为被private修饰的方法对子类不可见,后者是因为尽管被 protected修饰的方法可以被子类见到,也可以被子类重写,但是它是无法被外部所引用的,一个不能被外部引用的方法,怎么能谈多态呢。

5.方法绑定

  • 执行调用方法时,系统根据相关信息,能够执行内存地址中代表该方法的代码。分为静态绑定和动态绑定。
    • 静态绑定。在编译期完成,可以提高代码执行速度。
    • 动态绑定。通过对象调用的方法,采用动态绑定机制。这虽然让我们编程灵活,但是降低了代码的执行速度。这也是Java比C/C++速度慢的主要因素之一。Java中除了final类、final方、static方法,所有方法都是JVM在运行期才进行动态绑定的。

6.instance of和类型转换

  • instance of
public class Person{
    public void run(){}
    }
}
public class Student extends Person{
}
public class Teacher extends Person{
}

public class test {
   	//Object-->Person-->Student
    //Object-->Person-->Teacher
    public static void main(String[] args) {
    	Object o = new Student();
        System.out.println(o instanceof Student);//true
        System.out.println(o instanceof Person);//true
        System.out.println(o instanceof Object);//true
        System.out.println(o instanceof Teacher);//false
        System.out.println(o instanceof String);//false
        System.out.println("=====================");
        Person p = new Student();
        System.out.println(p instanceof Student);//true
        System.out.println(p instanceof Person);//true
        System.out.println(p instanceof Object);//true
        System.out.println(p instanceof Teacher);//false
        //System.out.println(p instanceof String); 编译报错
        System.out.println("=====================");
        Person q = new Person();
        System.out.println(q instanceof Student);//false
        System.out.println(q instanceof Person);//true
        System.out.println(q instanceof Object);//true
        System.out.println(q instanceof Teacher);//false
        //System.out.println(q instanceof String); 编译报错
    }
}
//System.out.println(x instanceof Y); 
//该代码能否编译通过,主要是看声明变量x的类型和Y是否存在子父类的关系.有子父类关系就编译通过, 没有子父类关系就是编译报错。
//System.out.println(x instanceof Y);
//输出结果是true还是false,主要是看变量x所指向的对象实际类型是不是Y类型的"子类型"。
  • 类型转换
public class Person{
    public void run(){}
    }
public class Student extends Person{
	public void go(){}
}
public class Teacher extends Person{
}

public static void main(String[] args) {
    //编译报错,因为p声明的类型Person中没有go方法
    Person p = new Student();
    p.go();
    //需要把变量p的类型进行转换
    Person p = new Student();
    Student s = (Student)p;
    s.go();
    //或者下面这种形式,注意这种形式前面必须要俩个小括号
    ((Student)p).go();
    /*
        编译通过 运行没问题
        Object o = new Student();
        Person p = (Person)o;
        编译通过 运行没问题
        Object o = new Student();
        Student s = (Student)o;
        编译通过,运行报错
        Object o = new Teacher();
        Student s = (Student)o;
    */
    
    //X x = (X)o;
	//运行是否报错,主要是变量o所指向的对象实现类型,是不是X类型的子类型,如果不是则运行就会报错。
}
  • 总结
    • 父类引用可以指向子类对象,子类引用不能指向父类对象。
    • 把子类对象直接赋给父类引用叫向上转型(upcasting),向上转型不用强制转型。 如Father father = new Son();
    • 把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son)father。
    • upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类方法有效。
public class Person {
    private String name;
    public void show(){
        System.out.println("我是人");
    }
}
public class Student extends Person{
    public void show(){
        System.out.println("我是学生");
    }
}
public static void main(String[] args) {
    Person person=new Student();//upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类方法有效
    person.show();
}
//输出结果:我是学生

六.接口和抽象类

1.abstract修饰符

  • abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
  • 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
  • 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
  • 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
//抽象类
public abstract class Action{
    //抽象方法
    public abstract void doSomething();
    }
}

//子类继承父类
public class Sleep extends Action{
	//实现父类中没有实现的抽象方法
	public void doSomething(){
	//code
	}
}

public static void main(String[] args) {
    //编译报错,抽象类不能new对象
    Action a = new Action();
    
    //调用子类的重写方法
    Action a = new Sleep();
	a.doSomething();
}

2.接口和抽象类的区别

  • 抽象类也是类,除了可以写抽象方法以及不能直接new对象之外,其他的和普通类没有什么不一样的。接口已经另一种类型了,和类是有本质的区别的,所以不能用类的标准去衡量接口。
  • 声明类的关键字是class,声明接口的关键字是interface。
  • 抽象类是用来被继承的,Java中的类是单继承。 类A继承了抽象类B,那么类A的对象就属于B类型了,可以使用多态 一个父类的引用,可以指向这个父类的任意子类对象。
  • 接口是用来被类实现的,Java中的接口可以被多实现。 类A实现接口B、C、D,那么类A的对象就属于B、C、D类型了,可以使用多态,一个接口的引用,可以指向这个接口的任意实现类对象

3.接口的特征

  • 接口中的方法都是抽象方法。接口中的变量都是静态常量(public static final修饰)。
public interface Behavior{
    public static final String NAME = "jasper";
	//默认就是public static final修饰的
	int AGE = 18;
    
	public abstract void run();
	//默认就是public abstract修饰的
	void eat();
	public void sleep();
}
  • 一个类可以实现多个接口
public interface A {
    public abstract void run();
}

public interface B {
    public abstract void sleep();
}
//Student需要实现接口A,B中所有的抽象方法
//否则Student类就要声明为抽象类,因为有抽象方法没实现
public class C implements A,B{
    @Override
    public void run() {
        System.out.println("run");
    }

    @Override
    public void sleep() {
        System.out.println("sleep");
    }
}

public static void main(String[] args) {
    A a=new C();//A只能调用接口A中声明的方法以及Object中的方法
    B b=new C();//B只能调用接口B中声明的方法以及Object中的方法
    a.run();
    b.sleep();
    
    if(a instanceof B){
		((B)a).sleep();
	}
}
//输出结果:
//run
//sleep
//sleep
  • 一个接口可以继承多个父接口
public interface A{
    public void testA();
}
public interface B{
    public void testB();
}
//接口C把接口A,B中的方法都继承过来了
public interface C extends A,B{
	public void testC();
}

//Student相当于实现了A B C三个接口,需要实现所有的抽象方法
//Student的对象也就同时属于A类型 B类型 C类型
public class Student implements C{
    public void testA(){}
    public void testB(){}
    public void testC(){}
}

public class Teacher{
    
}

public static void main(String[] args) {
    C o = new Student();
    System.out.println(o instanceof A);//true
    System.out.println(o instanceof B);//true
    System.out.println(o instanceof C);//true
    System.out.println(o instanceof Student);//true
    System.out.println(o instanceof Object);//true
    System.out.println(o instanceof Teacher);//false
    //编译报错
    System.out.println(o instanceof String);//String类final修饰
    //System.out.println(o instanceof X);
    //如果o是一个接口类型声明的变量,那么只要X不是一个final修饰的类,该代码就能通过编译,至于其结果
	//是不是true,就要看变量o指向的对象的实际类型,是不是X的子类或者实现类了
}

4.接口的作用

  • 接口的最主要的作用是达到统一访问,就是在创建对象的时候用接口创建 【接口名】 【对象名】= new 【实现接口的类】 这样你像用哪个类的对象就可以new哪个对象了,不需要改原来的代码。
  • 假如我们两个类中都有个function()的方法,如果我用接口,那样我new a();就是用a的方法,new b();就是用b的方法 这个就叫统一访问,因为你实现这个接口的类的方法名相同,但是实现内容不同。

5.接口的总结

  • Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用"_"分隔)。
  • Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化。
  • Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法。
  • 一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口。
  • Java接口必须通过类来实现它的抽象方法。
  • 当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类。
  • 不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例。
  • 一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承。
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值