转载

Java面向对象基础总结篇2

本文主要针对面向对象的三大特征:继承、封装、多态进行详细的总结。

继承

为什么需要继承?

从英文字面意思理解,extend的意思是”“扩展”,继承让我们更加容易实现类的扩展,继承让子类继承了父类的特征和行为,实现了代码的重用,不用再重新发明轮子。

生活中的继承

继承符合的关系符合的逻辑:“XX是一种XX”,只要这种东西能说通,就是一种继承关系。

Java面向对象基础总结篇2

如图:哺乳动物是一种动物,这能说得通,说明哺乳动物是从动物继承过来的;猫;是一种哺乳动物,这也能说得通,说明猫是从哺乳动物继承过来的。

//示例
public class Test {
  public static void main(String[] args) {
    Student s = new Student(18,"芮耳朵","Java");
    s.study();//继承了父类Person中的方法
    s.play();
  }
}
class Person {
  int age;
  String name;
  public void study() {
    System.out.println("每天进步一点点!");
  }
}
class Student extends Person {
  String major;
  public void play() {
    System.out.println("我喜欢打篮球");
  }
  //构造方法
  public Student(int age, String name, String major) {
    //拥有父类的所有属性
    this.age = age;
    this.name = name;
    this.major = major;
  }
}

/*运行结果是:喜欢Java,每天进步一点点!
             我喜欢打篮球*/

复制代码

instanceof运算符

instanceof是双元运算符,中文解释为“...的例子”,左边的操作元是对象,右边是一个类;当左边的对象是右面的类创建的对象时,返回true,否则返回false

//示例
public class Test {
  public static void main(String[] args) {
    Student s = new Student(18,"芮耳朵","Java");
    System.out.println(s instanceof Person);
    System.out.println(s instanceof Student); 
  }
}
//运行结果都是true。复制代码

继承的总结要点

  • Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链变得复杂,系统难以维护。
  • Java中类没有继承类,接口有多继承。
  • 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见得可以直接访问,比如父类私有的属性和方法。
  • 如果没有定义一个类时,没有调用extends,则它的父类是:java.lang.Object。

方法的重载(overload)

方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。 调用时,会根据不同的参数自动匹配对应的方法。

重载的方法,实际是完全不同的方法,只是名称相同而已!

构成条件:形参类型、新参个数、新参顺序不同

//示例
public static int add(int n1, int n2) 与 public static int add(int n1, int n2, int n3)
//方法名相同,参数个数不同,构成重载。
public static int add(int n1, int n2) 与 public static double add(double n1, int n2)
//方法名相同,参数类型不同,构成重载。
public static int add(int n1, int n2) 与 public static double add(int n1,double n2)
//方法名相同,参数顺序不同,构成重载。复制代码
//示例:只有返回值不同不构成方法的重载
public static int add(int n1, int n2) 与 public static double add(int n1, int n2)
//只有返回值不同,不构成重载。
public static int add(int n1, int n2) 与 public static int add(int num1,int num2)
//只有参数名称不同,不构成重载。复制代码

Object类基本特性

Object类是所有Java类的根基类,所有的Java对象都拥有Object类的属性和方法。Java只支持单继承,子类只能继承一个父类,父类再有父类也只能有一个,Java为方便组织类,提供了一个最根上的类,相当于所有的类都是从这个类继承而来,就是Object类。 如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。

这里介绍下 什么是哈希编码 ,后面会涉及到。

一个程序运行的时候,可能会有很多的对象在内存里分配,对于虚拟机来说,它运行的时候需要找到这些对象的地址,Java虚拟机会用一张表纪录每一个对象在什么位置上,这张表一般是使用哈希编码来记录,每个对象都有自己唯一的哈希编码,体现了一种数据内容和数据存放地址之间的映射关系。但是Java本身对哈希编码的实现有点问题,它可能有两个对象,内容不同,但是两者的哈希编码可能是一样的,不同的对象计算哈希编码的时候,可能会引起冲突,所谓的哈希冲突。

Object类方法

一个字符串和另外一个类型连接的时候,另外一种类型会自动转换成String类型,然后和字符串连接。 基础类型数据int,float,double转换成字符串比较简单,按照它们的数字转换即可,要是引用类型怎么办?Student s1 = new Student;一个字符串加上这个s1,你就不知道要怎么把这个p转换成字符串了,因为s1是一个引用类型。这时候toString方法就有用了。

//toString方法:该方法在打印对象时被调用,将对象信息变为字符串返回,默认输出对象地址
//toString方法源码:
public String toString() {
  return getClass().getName() + "@" + Interger.toHexString(hashCode());
  //默认会返回"类名+@+16进制的hashcode"。
  //在打印或者用字符串连接对象时,会自动调用该对象的toString()方法。
}


//示例:toString方法未重载之前
class Person {
    int age;
    String name;
public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        p.age = 18;
        p.name = "芮耳朵";
        //自动调用了toString()方法,简写。
        System.out.println(p);
        //不简写
        System.out.println(p.toString());
      
   }
}
/*运行结果:Person@2a84aee7
           Person@2a84aee7 */
---------------------------------------------------
//示例:toString重载之后
class Person {
    int age;
    String name;
    @Override
    public String toString() {
        return name+",年龄:"+age;
    }
}
public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        p.age = 18;
        p.name = "芮耳朵";
        System.out.println(p);
        System.out.println(p.toString());

    }
}
/*运行结果:芮耳朵,年龄:18
           芮耳朵,年龄:18 */复制代码

super关键字

在Java类中,当new一个对象出来的时候,这个对象会产生一个this的引用,这个this引用指向对象本身。如果new出来的对象是一个子类对象的话,那么这个子类里面还有一个super引用,这个super指向当前对象里面的父类。相当于在这个程序里,我们使用super来指向当前子类对象里面的父类/基类/超类,用this来指向对象自己。

//示例
class FatherClass {
  public int age;
  public void f() {
      age = 60;
      System.out.println("父类的年龄=" + age);
    }
}
class ChildClass extends FatherClass {
  //子类除了继承父类所具有的age属性外,又声明一个,此时的子类拥有两个age属性。
  public int age;
  //在子类ChildClass里面重写了由父类继承下来的f()方法的方法体。
  public void f() {
    super.f();
    //这里的super作为父类对象的引用对象用来调用父类对象里面的f()方法
    //打印输出语句  
    age = 20;//这个value是子类自己定义的age,不是从父类继承下来的那个age
    System.out.println("子类的年龄=" + age);
    //打印出来的是子类自定义的age值,值为20。
    System.out.println(age);
    System.out.println(super.age);
    }
}
复制代码
//测试类
public class Test {
    public static void main(String[] args) {
        ChildClass cc = new ChildClass();
        cc.f();
    }
}
/* 父类的年龄=60
   子类的年龄=20
   20
   60 */复制代码

封装

封装可以隐藏对象的内部实现,就是将对象的属性和方法封装成一个独立的整体,调用的时候只要知道如何调用即可,而且内部的改进不会影响外部调用。就好像我们使用遥控器,只需要知道怎么操作就行了,没有必要了遥控器的内部结构。

为什么要封装?

  • 为了提高了安全性,防止该类的代码和数据被外部随机的访问修改而出现问题,比如当我们在程序中private化age属性,并对外提供了get和set方法,当外界使用set方法设置属性值的时候,可以在set方法里面做个if判断,把age值限制在正常值内,以这种方式就不能随意赋值。
  • 为了方便重复使用代码,比如封装好的各种方法,可以随意调用,不用每处都重复方法的具体细节,已经封装好了,你只需要会调用即可,减少了代码量,显得简洁。

封装的实现

Java中使用访问控制符来控制哪些细节需要封装,哪些细节需要暴露。一共有四种访问控制,分别是 private、default、protected、public ,我们需要利用他们尽可能让访问权降到最低,从而提高安全性。

private: 表示私有的,是最严格的访问级别,只有自己类可以访问,使用对象是 变量与方法 。private修饰的变量只能通过类中公共的get方法被外部访问。

public class Person {
  private int age;
  public int getAge() {  //通过get方法来访问private修饰的变量。
    return this.age;  
 }
  public void setAge(int age) {
    this.age = age;  //通过set方法来修改private修饰的变量。
  }
}复制代码

default: 表示默认,即什么都不写不使用任何修饰符,只有同一个包的类能访问,使用对象是 类、接口、变量与方法

//示例
int i = 1;  //没有任何修饰符修饰int。
boolean isTall() {  //没有任何修饰符修饰方法boolean。
  return true;
  }复制代码

protected: 表示可以被同一个包的类以及以及其他包中的所有子类访问,这样的话就保护了不相关的类使用这些方法和变量了。使用对象是 变量与方法 。接口及接口的成员变量和方法不能声明为protected。

//示例
class Person {
  protected boolean isTall() {
    return true;
  }
}

class Player extends Person {
  protected boolean isTall() {
    //此处省略...重写父类的isTall()方法。
  }
}

//如果把isTall()方法声明为private,那么除了Person之外的类将不能访问。
//如果把isTall()方法声明为public,那么所有的类都可以访问该方法。
//如果只想让isTall()方法对其所在类的子类可见,则将该方法声明为protected。复制代码

public: 表示可以被该项目的所有包中的所有类所访问,使用对象是 类、接口、变量与方法

//示例
//Java程序的main()方法必须设置成public,否则Java解释器将不能运行该类。
public static void main(String[] args) {
  //代码...
  }复制代码

访问控制和继承规则

父类中声明的public的方法在子类中也必须为public。

父类中声明的protected的方法在子类中要么声明为protected,要么声明为public,不能为private。

父类中声明的private的方法不能被继承。

非访问修饰符

static 修饰符

静态变量:用static 关键字来声明的成员变量为静态变量,也称为类变量,它为该类的公共变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝,因此通过静态引用修改其属性值,结果是所有对象中的属性值都被修改。 要注意的是局部变量不能声明为static变量

静态方法:用static声明的方法为静态方法,可以通过类名称直接访问,非静态方法可以调用static声明的属性或者方法,而 static声明的方法不能调用非static声明的属性或者方法 ,因为非静态变量在通过new创建对象而初始化的,而static类型的方法在对象还没有创建的时候就可以被类名调用,因此在对象还没创建的情况下,如果使用static静态方法调用了非静态方法的属性或者方法,在逻辑顺序上就说不通了,所以在static方法中不可以访问非static的成员。

final修饰符

使用final声明的类不能被继承 ,没有类能够继承final的任何特征。使用final声明的方法内容不能被重写 override (父类和子类有同样的方法名和参数),但是可以被重载 overload (方法名相同,参数不同,最常用的即是构造方法的重载)。 使用final声明的变量即是常量不可以进行修改 ,使用final声明变量时,字母需要全部大写。如果一个程序中的变量使用public static final声明,则该变量将成为全局常量。

Java面向对象基础总结篇2 图:不能重写final修饰的方法

Java面向对象基础总结篇2 图:不能重写final修饰的方法

abstract修饰符

抽象方法:使用abstract修饰的方法, 没有方法体,只有声明 。定义的是一种 “规范” ,就是告诉子类必须要给抽象方法提供具体的实现。

抽象类: 包含抽象方法的类就是抽象类 。通过abstract方法定义规范,要求任何继承抽象类的子类必须实现父类所有抽象的方法(除非该子类也是抽象类)。抽象方法的声明以分号结尾,例如:abstract void study();

//示例
abstract class Person {
  abstract public void study();  //抽象方法
}
class Student extends Person {
  //子类必须实现父类的抽象方法,否则编译出错。
  public void study() {
    System.out.println("每天进步一点点!");
  }
}
//测试抽象类
public class Test{
  public static void main(String[] args) {
    Student s1 = new Student();
    s1.study();
  }
}复制代码

抽象类使用要点归纳

抽象类只能被继承。

抽象方法是需要声明不需要实现。

包含一个抽象方法的类只能定义成抽象类。

抽象类中的抽象方法不要使用private声明。

抽象类和抽象方法都要使用abstract关键字声明。

抽象类不能实例化,即不能用new来实例化抽象类。

抽象方法必须被子类实现,子类只要不是抽象类,必须重写抽象类中的全部抽象方法。

多态

多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“锻炼”方法,孙杨是游泳,姚明是篮球,马龙是打乒乓球。事物在运行过程中可能存在不一样的行为。

多态存在的三个必要条件:

1.子类继承父类

2.子类重写父类方法

3.父类引用指向子类对象,比如Parent p = new child();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,说明是子类新定义的方法,此时编译会出错;如果有,再去调用子类的同名方法,而不是父类的实现,这就是多态。

//父类
public class Athlete {
  int age = 10;
  public void exercise() {
    System.out.println("运动员锻炼");
  }
}
//子类
class XunYang extends Athlete {
  int age = 20;
  public void exercise() {
    System.out.println("孙杨游泳锻炼");
  }
  public void run() {
    System.out.println("孙杨正在跑步");
  }
}

//子类觉得父类方法不够好,重写一个!
class YaoMing extends Athlete {
  int age = 30;
  public void excercise() {
    System.out.println("姚明打篮球锻炼");
 }
}

class MaLong extends Athlete {
  int age = 40;
  public void exercise() {
    System.out.println("马龙打乒乓球锻炼");
  }
}复制代码
//测试类1
public class Test {
    public static void main(String[] args) {
        Athlete a = new XunYang();
        a.excercise();
        // a.run(); 编译报错,需要向下强制转型
        System.out.println("年龄是" + a.age);
    }
}
/* 运行结果:孙杨游泳锻炼
            年龄是10 */
复制代码

当满Java多态的三个条件时,可以发现a.exercise()调用的实际上是子类的exercise方法,但a.age调用的还是父类的age,而a.run()则不会通过编译。

//测试类2
public class Test {
  public static void main(String[] args) {
    Athlete a1 = new XunYang();  //向上自动转型
    SportsExercise(a1);
    Athlete a2 = new YaoMing();
    SportsExercise(a2);
    Athlete a3 = new MaLong();
    SportsExercise(a3);
      
    //由于这里调用的是自己新的方法,是父类没有的方法。
    //a1.run();编译报错,这里必须要强制向下转型。
    XunYang ath = (XunYang) a1;  
    ath.run();
    System.out.println(("年龄是" + a1.age));  }

  //这里就可以看做是同一个方法“锻炼”,产生多态。
  //如果没有多态,我们这里需要写很多重载的方法
  //每增加一个运动员,就需要重载一个锻炼的方法,非常麻烦
  /* static void Athlete(XunYang x) {
       x.exercise;     
     }
     static void Athlete(YaoMing y) {
       y.exercise;
     }
     ... */
     
    static void SportsExercise(Athlete a) {
    a.exercise();
    }
}
/* 运行结果:孙杨游泳锻炼
            姚明打篮球锻炼
            马龙打乒乓球锻炼  
            孙杨正在跑步
            年龄是10  */复制代码

上面是多态最为多见的一种用法,用父类当做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。

原文  https://juejin.im/post/5d4bc40651882523942f6b17
正文到此结束
Loading...