Java面向对象复习

Posted 爱敲代码的三毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面向对象复习相关的知识,希望对你有一定的参考价值。

文章目录


一、类和对象

1. 面向对象

面向过程和面向对象?

C语言就是典型的面向过程编程,它关注的实现过程。

Java是一个面向对象的语言,面向对象关注的是对象的创建和维护对象之间的关系,而不关系这个对象是怎么实现的。

举个列子:如果你要洗衣服

面向过程:拿盆子接水,放洗衣粉,手搓,再接水洗,把水倒掉拧干

面向对象:把衣服丢进洗衣机,放入洗衣粉,打开洗衣机

在这个过程中,洗衣机就可以当做一个对象,我们不必关心它的实现,只需拿过来用就可以了。

2. 类的定义

通过class关键字就能创建一个对象。

class Student 
    String name;
    int age;
    public void show() 
        System.out.println("haha");
    

  • 在类中定义的变量,称为成员变量或者字段。
  • 在类中定义的方法,称为成员方法

注意:成员变量是在类的内部,方法的外部

3. 对象的创建

用类创建对象的过程成为实例化对象,通过new关键字实例化一个对象。我们可以把类当做一个图纸或者模板,通过一个图纸可以建很多的房子,也就是说一个类可以实例化多个对象。

Student std1 = new Student();
Student std2 = new Student();
Student std3 = new Student();

匿名对象(只能使用一次)

没有引用的对象称为匿名对象.

new Student();

4. 类在内存中的存储

类和数组一样都属于引用,而引用指向的是一个对象,像上面的std1和std2都属于引用,局部变量是在栈上开辟内存的,引用里面存的是对象的地址,这些变量里存的都是引用,每实例化一个对象句在堆区开辟一块内存,对象里存的就是成员变量。

注意:类里面的方法并不是存在堆区的,方法本身是不占用内存的,方法是存在方法区的方法表中,在每个对象new好之后前面又几个字节存的就是方法表的地址,只有在调用这个方法时,才会通过方法表的地址找到这个方法,然后为这个方法在栈上开辟栈帧。


class Student 
    //成员变量
    String name;
    int age;
    // 成员方法
    void show() 
        System.out.println("haha");
    

public class Main 

    public static void main(String[] args) 
        Student std1 = new Student();
        Student std2 = new Student();
        Student std3 = new Student();
    



通过上图发现,其实并不是所有引用都是在栈上的,如果它是一个实例成员变量,那么它就是在堆上的,比如上图的name和age。

5.类的成员使用

成员变量的初始化,可以给类中的字段给一个默认值,但是不建议。

class Student 
    //成员变量
    String name = "zhangsan";
    int age = 10;
    // 成员方法
    void show() 
        System.out.println("haha");
    

更建议在使用时进行赋值

public static void main(String[] args) 
        Student std1 = new Student();
        std1.name = "李四";
        std1.age = 18;
    

6. toString()方法

如果我们直接打印一个对象可能会出现下面的情况

public static void main(String[] args) 
        Student std1 = new Student();

        System.out.println(std1);
    

打印结果

Student@1b6d3586

因为如果我们的类没有自己实现toString方法,那么它将会调用Object的**toString()**方法,该方法是打印的是类名+@+hash值

public String toString() 
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    

那么我们就可以重写(@Override)toString方法


class Student 
    //成员变量
    String name;
    int age;
    // 成员方法
    void show() 
        System.out.println("haha");
    

    @Override
    public String toString() 
        return "Student" +
                "name='" + name + '\\'' +
                ", age=" + age +
                '';
    

那么打印的时候就是调用我们重写的toString方法,如果成员变量没有赋值打印出来的默认值。

@Override是一个注解,它表示该方法是重写了父类的方法。

7. static 关键字

我们把成员变量分为普通成员变量和静态成员变量,普通成员变量也叫做实例成员变量,静态成员变量也叫做类变量

静态成员变量

count是被static关键字修饰,它就是一个静态成员变量。我们可以通过对象来访问,也可以通过类名来访问。但是为了规范建议使用类名来访问静态成员变量,因为静态成员变量是不 依赖对象的。

class Student 
    //成员变量
    String name;
    int age;
    static int count;
    // 成员方法
    void show() 
        System.out.println("haha");
    

public class Main 

    public static void main(String[] args) 
        Student std1 = new Student();
        System.out.println(std1.count);
        System.out.println(Student.count);
    


为了验证静态成员变量是不依赖对象的,可以写出这么一段代码

class Student 
    //成员变量
    String name;
    int age;
    static int count;
    // 成员方法
    void show() 
        System.out.println("haha");
    

public class Main 

    public static void main(String[] args) 
        Student std1 = new Student();
        std1.count++;
        Student std2 = new Student();
        std2.count++;
        System.out.println(Student.count);
        Student std = null;
        System.out.println(std.count);
    


打印结果

2
2

说明std1和std2操作的都是同一个count,std=null表示std不指向任何对象,而因为静态成员变量是不依赖于对象的所以最后一行代码是可以正常输出的。

所以

  • 静态成员变量是不依赖于对象的
  • 访问静态成员变量的方式更建议类名.成员变量

静态成员方法

静态成员方法也是通过类名来访问使用的。

class Student 
    //成员变量
    String name;
    int age;
    static int count;
    // 成员方法
    static void show() 
        System.out.println("haha");
    

需要注意的是:

  • 在静态成员方法里是不能使用普通成员变量的
  • 同理在普通成员方法里也是不能使用静态成员变量的
  • 同样的静态成员方法里是不能使用普通成员方法的

因为静态成员变量和成员方法它们都是不依赖于对象的,普通成员变量和方法都是依赖于对象的,如果在静态里使用此时对象都还没有创建出来何来的成员呢?显然是不合理的。

但在普通成员方法里是可以使用普通成员变量的,因为想用普通成员方法,就得实例化对象。

只要被static所修饰的成员变量就在方法区

二、封装

在面向对象的语言里,封装是其特征之一。

封装的本质就是让类的调用者不必太多的了解类的实现者是如何实现类的,
把属性和动作隐藏,只提供相应的方法来调用即可,只要知道如何使用类就行了.
当类的实现者把内部的逻辑发生变化时,类的调用者根本不用因此而修改方法。
这样就降低了类使用者的学习和使用成本,从而降低了复杂程度,也保证了代码的安全性。

1. 构造方法

概念基本使用

构造方法是一种特殊方法,使用关键字new示例化对象的时候会被自动调用,用于完成初始化操作

  • 构造方法没有返回值,且方法名要和类名相同
  • 构造方法是用来构造对象的(实例化)

new的执行过程

  1. 为对象分配内存空间
  2. 调用合适的构造方法

每一个类都有一个默认的没有参数的构造方法,如果我们没有写构造方法,系统默认会有一个没有参数且没有任何内容的构造方法,比如下面这个代码就有一个默认的构造方法,当然如果你已经写了构造方法这个默认的就不存在了。

class Student 
    //成员变量
    public String name;
    public int age;
    

构造方法的重载

方法重载的几个要求并不陌生,构造方法也是能重载的

  • 方法名相同
  • 参数类型和个数不相同
  • 返回值不做要求
class Student 
    //成员变量
    public String name;
    public int age;
    public Student() 
        System.out.println("无参构造方法");
    
    public Student(String s) 
        System.out.println("一个参数的构造方法");
    
    public Student(String name, int age) 
        System.out.println("两个参数的构造方法");
    

public class Main 

    public static void main(String[] args) 
        Student s1 = new Student();
        Student s2 = new Student("test");
        Student s3 = new Student("test",20);
    


执行结果

无参构造方法
一个参数的构造方法
两个参数的构造方法

this关键字

this关键字一共有三种用法

  • this.data (访问当前对象的成员变量)
  • this.func() (访问当前对象的成员方法)
  • this.() (访问当前对象自己的构造方法)

有的书上说 this 代表当前对象,其实这样的说法并不准确。
正确的说法应该是:this表示当前对象引用,为什么这么说呢?

下面这段代码通过构造方法给成员变量赋值,而一个对象的创建要等构造方法执行完毕后才创建,所以this表示的是当前对象的引用

class Student 
    //成员变量
    String name;
    int age;

    public Student(String name, int age) 
        this.name = name;
        this.age = age;
    

this关键字也可以访问成员方法

class Student 
    //成员变量
    String name;
    int age;

    public Student(String name, int age) 
        this.name = name;
        this.age = age;
    
    public void print() 
        System.out.println("haha");
    

public class Main 

    public static void main(String[] args) 
        Student s1 = new Student("张三",20);
        s1.print();
    


this调用构造方法

注意:this调用构造方法一定要放在构造方法的第一行,每一个构造方法里只能使用一次

class Student 
    //成员变量
    public String name;
    public int age;
    public Student() 
        this("haha");
        System.out.println("无参构造方法");

    
    public Student(String s) 
        System.out.println("一个参数的构造方法");
    
    public Student(String name, int age) 
        System.out.println("两个参数的构造方法");
    

public class Main 

    public static void main(String[] args) 
        Student s1 = new Student();
        
    


运行结果

一个参数的构造方法
无参构造方法

2. private

通过private关键字实现封装.被private修饰的成员变量和方法就只能在当前类中使用。在类外是无法直接访问的。和它对应的就是public关键字,被public修饰的成员变量和成员方法是在任何地方都是可以访问的。

成员变量被private修饰后,一般要提供对应的get和set方法(根据需求),一个用来设置值一个用来获取值,这两个方法都是public的。

这样外部就无法直接访问成员变量了,只能通过get和set方法来访问。

class Student 
    //成员变量
    private String name;
    private int age;

    public Student(String name, int age) 
        this.name = name;
        this.age = age;
    

    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;
    

构造方法也是可以被private修饰的,在单例设计模式中会使用到。下面就是一个简单的饿汉单例

class Test 
    private static Test test = new Test();

    private Test() 

    
    public static Test getExample() 
        return test;
    

3. 封装的好处

  1. 提高了数据的安全性
    别人不能够通过 变量名来修改某个私有的成员属性
  2. 操作简单
    封装后,类的调用者在使用的时候,只需调用方法即可。
  3. 隐藏了实现
    实现过程对类的调用者是不可见的,类的调用者只需调用方法即可,不知道具体实现

三、继承

1. 继承的概念

继承主要的意义就是为了代码的复用。

当我们定义的一些类存在了一些联系,简单来说就是多个类之间存在着大量重复的代码,为了消除代码的冗余就可以使用继承。

比如下面的代码,存在着大量代码的冗余,且这些类中存在着相同的方法和字段。

class Animal 
    public String name;

    public void eat(String food) 
        System.out.println(this.name + "正在吃" + food);
    

class Dog 
    public String name;
        
    public void eat() 
        System.out.println(this.name+"吃东西");
    

class Bird 
    public String name;
    
    public void eat() 
        System.out.println(this.name+"吃东西");
    
    
    public void fly() 
        System.out.println(this.name+"起飞");
    

2. extends

在Java中通过extends实现继承

class 子类 extends 父类 


  • Java中只支持单继承,一个子类只能继承一个父类
  • 子类会继承父类的所有非private修饰的字段和方法
  • 对于父类的private的字段和方法,子类是无法访问的,并不是没有继承而是被private修饰只能在当前类中使用。
  • 子类实例中包含着父类的实例,可以使用super关键字得到父类实例的引用

此时通过继承关系把上面的代码进行改造,让Dog类和Bird类继承Animal类。

class Animal 
    public String name;

    public void eat(String food) 
        System.out.println(this.name + "正在吃" + food);
    

class Dog extends Animal 



class Bird extends Animal 


    public void fly() 
        System.out.println(this.name+"起飞");
    

public class TestDemo2 

    public static void main(String[] args) 
        Dog dog = new Dog();
        Bird bird = new Bird();
        dog.name = "二哈";
        bird.name = "鸟";
        dog.eat("饭");
        bird.eat("饭");
    

运行结果

二哈正在吃饭
鸟正在吃饭

此时,像Animal这样被继承的类,我们称为父类、基类或者超类,对于像Cat和Bird这样

继承Animal的类,叫做子类或者派生类

就和现实中儿子继承父亲类似,子类会继承父类的字段和方法,从而达到代码复用的效果。

此时在内存中的存储

像成员方法和静态的成员变量都是存在方法区的,类里面的方法并不是是存在堆区的 ,方法本身是不占内存的,方法是存在方法区里的方法表中,在每个对象 new 好之后前面有几个字节存的就是方法表的地址,只有在调用这个方法时,会通过方法表的地址找到这个这个方法,然后在栈上为这个方法开辟栈帧。

3. super关键字

我们知道在一个类没有写任何构造方法的时候,默认会有意的没有参数且没有任何内容的构造方法。

下面这个例子,当我们给父类手动写上一个参数的构造方法时,继承关系下的子类都报错。

当子类继承父类后,在构造子类之前,必须先帮父类进行构造

这时候就用到了super关键字。

super表示父类实例的引用,和this类似共有三种用法

  1. super.父类成员变变量

  2. super.父类成员方法

  3. super()调用父类的构造方法

注意:super和this一样不能在静态方法中使用

在用super关键字在子类的构造方法里帮父类构造的时候一定要在第一行

class Animal 
    public String name;
    public Animal(String name) 
        this.name = name;
    
    public void eat(String food) 
        System.out.println(

以上是关于Java面向对象复习的主要内容,如果未能解决你的问题,请参考以下文章

面向对象之鸭子类型

Python面向对象之鸭子类型

Day21 抽象接口多太鸭子类型

面向对象-鸭子类型

面向对象实例补充

Python面向对象编程——多态多态性鸭子类型