java面向对象

面向对象

  • 面向对象编程(Object-Oriented Programming,OOP)
  • 面向对象编程的本质是:以类的方式组织代码,以对象的组织(封装)数据。

什么是面向对象

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

面向对象和面向过程的区别

​ 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

创建和初始化对象

  • 使用new关键字创建对象时,除了分配内存空间外,还会给创建好的对象进行默认的初始化,以及对类中构造器的调用
  • Student.java
package com.kuangshen.OOP;
public class Student {
    String name;
    int age;
    public void study()
    {
        System.out.println(this.name + "在学习" + " 他已经" + this.age + "岁了");
    }
}
  • Application.java:
package com.kuangshen.OOP;

public class Application {
    public static void main(String args[])
    {
        Student student1 = new Student();
        Student student2 = new Student();
        student1.name = "小明";
        student1.age = 12;
        student2.name = "小红";
        student2.age = 11;
        System.out.println(student1.name + " " + student1.age);
    }
}

  • 类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。并且构造器有以下两个特点:

    • 必须和类的名字相同
    • 必须没有返回类型,也不能写void
  • 其作用如下:

    • 利用new调用构造器
    • 初始化对象的值
  • 案例如下:

    • Person.java

    • package com.kuangshen.OOP;
      
      public class Person {
          //一个类即使什么都不写也会有一个默认构造器方法。与类名相同并且没有返回值
          String name;
          int age;
          public Person() {
              this.name= "神州";//可以什么都不写
          }
      
          public Person(String name)
          {
              this.name = name;
          }
      
          public Person(int age, String name) {
              this.age = age;
              this.name = name;
          }
      }
      
    • Application.java

    • package com.kuangshen.OOP;
      
      public class Application {
          public static void main(String args[])
          {
              Person person = new Person();
              System.out.println(person.name);
          }
      }
      
      
  • 注意:定义有参构造之后,如果想要使用无参构造,我们需要显示的定义一个无参构造(在一般条件下,无参构造不用写,系统也会默认自带一个)。

    • 下面例子会报错,需要把Person.java中的无参构造的注释去掉:

    • package com.kuangshen.OOP;
      
      public class Person {
          //一个类即使什么都不写也会有一个默认构造器方法。与类名相同并且没有返回值
          String name;
          int age;
          /*public Person() {
              this.name= "神州";//可以什么都不写
          }*/
          public Person(String name)
          {
              this.name = name;
          }
      }
      
      
      package com.kuangshen.OOP;
      
      public class Application {
          public static void main(String args[])
          {
              Person person = new Person();
              System.out.println(person.name);
          }
      }
      
      

封装

  • 该露的露,该藏的藏

    • 我们程序设计要追去高内聚,低耦合。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。
  • 封装:数据的隐藏

    • 通常,应禁止直接访问一个对象中数据的直接表示,而应通过操作接口来访问,这称为信息隐藏。
  • 封装中提出了private关键字,利用这个关键字,可以避免对数据的直接表示,代码如下:

    • ackage com.kuangshen.OOP;
      
      public class Teacher {
          private int age;
          private String name;
          public void setName(String name)
          {
              this.name = name;
          }
          public String getName()
          {
              return this.name;
          }
      }
      
    • package com.kuangshen.OOP;
      
      public class Application {
          public static void main(String args[])
          {
              Teacher teacher = new Teacher();
              teacher.setName("天才");
              System.out.println(teacher.getName());
             // System.out.println(teacher.name);//不可以对数据直接操作
          }
      }
      

继承

  • 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

  • 继承一般使用extend关键字,其中people.java,people1.java和Application.java的例子如下:

    • package com.kuangshen.OOP;
      
      public class people {
          private int age;
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      }
      
    • package com.kuangshen.OOP;
      
      public class people1 extends people{
          public void say()
          {
              System.out.println(getAge());
          }
      }
      
    • 
      
      package com.kuangshen.OOP;
      
      public class Application {
          public static void main(String args[])
          {
              people1 person = new people1();
              person.setAge(5);
              person.say();
          }
      }
      
  • 继承类型

    • 需要注意的是 Java 不支持多继承,但支持多重继承。

    java面向对象

  • 继承的特性

    • 子类拥有父类非 private 的属性、方法。
    • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
    • 子类可以用自己的方式实现父类的方法。
    • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
    • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
  • 继承关键字

    • 继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
  • super关键字

    • super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

      this关键字:指向自己的引用。

      后面people.java,people1.java和Application.java的例子如下:

      package com.kuangshen.OOP;
      
      public class people {
          protected String name = "天下";
          public void print()
          {
              System.out.println("1");
          }
      }
      
      package com.kuangshen.OOP;
      
      public class people1 extends people{
          private String name = "月关";
          public void print()
          {
              System.out.println("2");
          }
          public void test(String name)
          {
              System.out.println(name);
              System.out.println(this.name);
              System.out.println(super.name);
          }
          public void test1()
          {
              print();
              this.print();
              super.print();
          }
      }
      
      package com.kuangshen.OOP;
      
      public class Application {
          public static void main(String args[])
          {
              people1 person = new people1();
              person.test("天马");
              person.test1();
          }
      }
      

      最终的结果如下:

      天马
      月关
      天下
      2
      2
      1
      
  • super构造器

    • super无参构造器

      package com.kuangshen.OOP;
      public class people1 extends people{
          public people1()
          {
              //隐藏代码,调用了父类的构造器
              super();//可加可不加,但是加了必须在第一行。
              System.out.println("使用了子类构造器");
          }
      }
      
      package com.kuangshen.OOP;
      public class people {
          public people()
          {
              System.out.println("使用了父类构造器");
          }
      }
      
      package com.kuangshen.OOP;
      public class Application {
          public static void main(String args[])
          {
              people1 person = new people1();
          }
      }
      
    • super有参构造器

      package com.kuangshen.OOP;
      
      public class people1 extends people{
          public people1()
          {
              //影藏代码,调用了父类的构造器
              super("人间");//当有参时,如果super不写,那么子类也要变成有参构造,如果写,super要变成有参构造
              System.out.println("使用了子类构造器");
          }
      }
      
      package com.kuangshen.OOP;
      
      public class people {
          public people(String a)
          {
              System.out.println("使用了父类" + a +"构造器");
          }
      }
      
      package com.kuangshen.OOP;
      
      public class Application {
          public static void main(String args[])
          {
              people1 person = new people1();
          }
      }
      
      
  • super注意点

  1. super调用父类的构造方法,必须在构造方法的第一个
  2. super只能出现在子类的方法或者构造方法中
  3. super和this不能同时调用构造方法
  • super Vs this

    1. 代表的对象不同

      • this:调用本身的对象
      • super:代表父类对象的应用
    2. 前提:

      • this:没有继承也可以使用
      • super:只能在继承条件才可以使用
    3. 构造方法:

      • this();本类的构造
      • super();父类的构造

重写

  • 加上static,以下分别为A.java,B.java和Application.java
package com.kuangshen.OOP;

public class A extends B{
    public static void test()
    {
        System.out.println("A");
    }
}
package com.kuangshen.OOP;

public class B {
    public static void test()
    {
        System.out.println("B");
    }
}
package com.kuangshen.OOP;

public class Application {
    public static void main(String args[])
    {
        //方法的调用只和左边,定义的数据类型有关
        A a = new A();
        a.test();
        B b = new A();//父类的引用指向了子类
        b.test();
    }
}

输出结果为

A

B

  • 重写的例子(重写只和非静态有关)

    package com.kuangshen.OOP;
    
    public class A extends B{
        //Override重写
        @Override//注解:有功能的注释
        public void test() {
            System.out.println("test=>A");
        }
    }
    
    package com.kuangshen.OOP;
    
    public class B {
        public void test()
        {
            System.out.println("test=>B");
        }
    }
    
    
    package com.kuangshen.OOP;
    
    public class Application {
        public static void main(String args[])
        {
            //方法的调用只和左边,定义的数据类型有关
            A a = new A();
            a.test();
            B b = new A();//非静态时,子类重写了父类的方法,结果只和子类的结果有关
            b.test();
        }
    }
    
    

    最终的结果为:

    A

    A

  • 重写的注意点:需要有继承关系,子类继承父类的方法

    1. 方法名必须相同
    2. 参数列表必须相同(否则就是重载)
    3. 修饰符:范围可以扩大但不可以缩小:public>Protected>Default>private
    4. 抛出的异常:范围可以缩小但不可以扩大
  • 重写,子类和父类必须要一致,但方法体不同

  • 为什么需要重写:父类的方法,子类不一定需要,或者不一定满足

多态

  • 多态,即同一方法根据发送对象的不同而采用不同的行为方式。
  • 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(父类、有关系的类)
  • Student.java,Person.java和Application.java的案例如下:
package com.kuangshen.OOP;

public class Student extends Person{
    @Override
    public void run() {
        System.out.println("Student");
    }
    public void eat()
    {
        System.out.println("eat");
    }
}
package com.kuangshen.OOP;

public class Person {
    public void run()
    {
        System.out.println("Person");
    }
}
package com.kuangshen.OOP;

public class Application {
    public static void main(String args[])
    {
        Student s1 = new Student();
        Person s2 = new Student();
        Object s3 = new Student();
        //对象能执行哪些方法,只能看对象左边的类型,和右边关系不大
        s1.run();
        s2.run();
        s1.eat();
        ((Student) s2).eat();//父类无法调用子类的方法,只能强制转换成子类
        //s2.eat;//会报错
    }
}

输出结果:

Student

Student

eat

eat

  • 多态注意事项:

    • 多态是方法的多态,属性没有多态
    • 多态两个类之间有继承关系,如父类和子类
    • 存在条件:两类之间继承关系;方法需要重写;父类的引用指向子类,如:Father f1 = new Son();
  • 以下方法无法重写,自然没有多态

    • static方法,属于类,它不属于实例
    • final常量
    • private方法

instanceof

直接看例子:

package com.kuangshen.OOP;

public class Application {
    public static void main(String args[])
    {
        Object object = new Student();
        System.out.println(object instanceof Student);
        System.out.println(object instanceof Person);
        System.out.println(object instanceof Object);
        System.out.println(object instanceof Teacher);
        System.out.println(object instanceof String);
        System.out.println("=======================");
        Person person = new Student();
        System.out.println(person instanceof Student);
        System.out.println(person instanceof Person);
        System.out.println(person instanceof Object);
        System.out.println(person instanceof Teacher);
        //System.out.println(person instanceof String);//编译失败
        System.out.println("=======================");
        Student student = new Student();
        System.out.println(student instanceof Student);
        System.out.println(student instanceof Person);
        System.out.println(student instanceof Object);
        //System.out.println(student instanceof Teacher);//编译失败
        //System.out.println(student instanceof String);//编译失败

        //Object > String
        //Object > Student > Person
        //Object > Teacher > Person
    }
}

我们可以发现,对于A a = new B()[其中A是B的父类或A = B]中,如果A和B没有父子关系,那么编译失败。在后面的(a instanceof C)中如果B是C的子类或者B=C,那么返回值为true,否则返回值为false

类型强制转换

先看例子:

package com.kuangshen.OOP;

public class Application {
    public static void main(String args[])
    {
        Person person = new Student();
        ((Student) person).eat();
    }
}

当父类转化为子类时,要强制转换,本质原因是子类可以调用父类的方法,但父类不可以调用子类的方法。

注意点:

  1. 父类引用指向子类的对象
  2. 把子类转换为父类,向上转型,不用强制转换
  3. 把父类转换为子类,向下转型,需要强制转换
  4. 强制转换的目的是为了方便方法的调用,减少重复的代码

static关键字

  • 静态static和类名一起加载在内存中,所以使用时有一些好用的特性。
package com.kuangshen.OOP;

public class Bus {
    private static int age;
    private double score;
    public void run()
    {}
    public static void go()
    {}
    public static void main(String args)
    {
        Bus bus = new Bus();
        System.out.println(Bus.age);//对于静态变量,可以用类名直接调用。一般工作时尽量这样调用,方便知道这个age是静态变量。
        //System.out.println(Bus.score);//报错
        System.out.println(bus.age);
        System.out.println(bus.score);
        bus.run();
        bus.go();//一般都是这样调用

        //Bus.run();//这样会报错
        Bus.go();//static的特性可以用类名直接调用
        go();//由于main函数在类名中,所以可以前面不加类名直接调用,所以这种格式不能在别的.java文件中这样写

    }
}
  • 静态代码块
package com.kuangshen.OOP;

public class Bus {
    //顺序2:一般用来赋初值
    {
        System.out.println("匿名代码块");
    }
    //顺序1:只执行依次
    static {
        System.out.println("静态代码块");
    }
    public Bus(){
        System.out.println("构造方法");
    }
    public static void main(String args[])
    {
        Bus bus1 = new Bus();
        System.out.println("=========");
        Bus bus2 = new Bus();
    }
}

最终输出结果如下:

静态代码块
匿名代码块
构造方法
@@@@@@@@@@@@@@@@@@@@
匿名代码块
构造方法

  • 静态导入包

final特性

如果一个类用了final关键字,就不能被继承。

package com.kuangshen.OOP;

public final class Person {
    public void run()
    {
        System.out.println("Person");
    }
}
package com.kuangshen.OOP;

public class Student extends Person{//这里会报错,无法继承有final关键字的(常量无法被继承)
    @Override
    public void run() {
        System.out.println("Student");
    }
    public void eat()
    {
        System.out.println("eat");
    }
}

抽象类

例子:

package com.kuangshen.OOP;
public abstract class Action {
    public abstract void dosomething();
}
package com.kuangshen.OOP;
public class ActionMain extends Action {
    //当一个子类继承了抽象类时会报错,必须要重写抽象方法。
    //如果子类也是抽象类,那么要在子子类去重写抽象方法
    @Override
    public void dosomething() {
        
    }
}

  • 不能new这个抽象类,只能靠子类去实现它。
  • 抽象类中可以写普通的方法。
  • 抽象方法必须写在抽象类中。

抽象类的用处:比如你开发一个游戏,不同的游戏任务某项数据不一样,那么利用抽象类方法名不变,只需要在其继承中重写方法就可以了,本质是为了提高开发效率。

抽象类需要继承,由于java中都是单继承,所以泛用性大大降低;因为接口可以多继承,所以java中最常用到的是接口。

接口

  • 接口可以理解为一种特殊的类,里面全部是由****全局常量*公共的抽象方法所组成。接口是解决***Java无法使用多继承*的一种手段,但是接口在实际中更多的作用是*制定标准*的。或者我们可以直接把接口理解为*100%的抽象类****,既接口中的方法****必须全部****是抽象方法。
  • 接口注意点:
    • 接口是为了定义一些方法,让不同的人去实现
    • 接口中默认的方法都是public abstract
    • 接口中的常量都是public static final
    • 接口和抽象类一样不能被实例化,因为接口中没有构造方法
    • 通过implements关键字可以实现多个接口
    • 实现接口时必须要重写接口里面的方法

下面是userService.java,timeService.java,userServiceImpl.java的例子

package com.kuangshen.OOP.demo01;

public interface UserService {//接口通过interface定义
    public abstract void a();//在IDEA中,接口中的方法都默认公共并且抽象的,所以public和abstract可以不写
    void add();
    void delete();
    void update();
    void query();
}

package com.kuangshen.OOP.demo01;

public interface TimeService {
    void timer();
}
package com.kuangshen.OOP.demo01;

public class UserServiceImpl implements UserService, TimeService {//接口可以多继承
    @Override
    public void a() { }
    @Override
    public void add() { }
    @Override
    public void delete() { }
    @Override
    public void query() { }
    @Override
    public void update() { }
    @Override
    public void timer() { }
}

内部类

package com.kuangshen.OOP.neibulei;

public class Outer {
    private int id = 10;
    public void out()
    {
        System.out.println("这是外部类的方法");
    }
    public class Inner
    {
        public void in()
        {
            System.out.println("这是内部类的方法");
        }
        //内部类可以获得外部类的私有属性
        public void getID()
        {
            System.out.println(id);
        }
    }
}
package com.kuangshen.OOP.neibulei;public class MainClass {    public static void main(String args[])    {        Outer outer = new Outer();        outer.out();        Outer.Inner inner = outer.new Inner();//内部类的定义方法        inner.in();        inner.getID();    }}

输出结果:

这是外部类的方法
这是内部类的方法
10

上一篇:面向对象编程OOP


下一篇:【go语言学习】面向对象oop