Java教程

面向对象编程(OOP)【感谢狂神说JAVA】

本文主要是介绍面向对象编程(OOP)【感谢狂神说JAVA】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

面向对象编程(OOP)

Java的核心思想就是OOP(Object-Oriented Programming)

OOP的概念

  • 面向过程:

    • 步骤清晰简单,第一步做什么,第二步做什么……
    • 面向过程适合处理一些比较简单的问题
  • 面向对象:

    • 物以类聚,分类的思维模式。
      • 首先考虑解决问题需要哪些分类
      • 对这些分类进行单独思考
      • 对某个分类下的细节进行面向过程的思索
    • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
    • 也就是说,面向对象实际上是对复杂的问题宏观把控、从整体分析,用面向对象的思路去分析整个系统,然后在微观操作中,用面向过程的思路去解决
  • 面向对象的本质:以类的方式组织代码。以对象的形式组织(封装)数据

  • 抽象:把一个系统的共同特点抽象抽取出来

    【编程思想,持续学习,茅塞顿开!多实践,多测试大脑中的想法】

  • 三大特征:

    • 封装
    • 继承
    • 多态:同一个事物的多种形态
  • 从认识论的角度理解:先有对象,后有类【从很多对象中抽象出来的特征,形成类】。对象,是具体的事物。类,是抽象的,是对对象的抽象。

  • 从代码运行的角度考虑:先有类,后有对象【先设计一个类,再生成对象,用new关键字】。类是对象的模板。

方法回顾

  • 一个真正的程序中,只有一个main方法
  • void方法中,可以不返回结果,也可以只写return
  • break结束循环,return结束方法
  • 一般一个完整的方法都会有异常处理
// 读文件时抛出输入输出异常
public void readFile(String file) throws IOException{

}

方法调用

  • 静态方法(static)和非静态方法

    // 对于静态方法
    Student.say();  //类名.方法名()
    // 对于非静态方法
    // 首先,将类实例化,通过new关键字
    new.Student.say();  // new.类名.方法名()
    // 常规写法
    Student student = new Student();  // 对象类型 对象名=对象值
    student.say();
    
    • 在同一个类中
      • 静态方法可以调用静态方法
      • 非静态方法可以调用静态方法
      • 非静态方法可以调用非静态方法
      • 但是静态方法不能调用非静态方法。因为static方法是和类同时加载的,它一直存在,而非静态方法只有在类实例化之后才存在。
  • 实际参数和形式参数

    • 形参和实参的类型要对应
  • 值传递和引用传递

    • java中都是值传递
    • Java中传递对象时,形似引用传递,因为它传递了它的地址,而地址中的内容被改变,但是地址并不改变。所以它本质还是值传递
    • 值传递:形参和实参在存储空间上是独立的两个变量;引用传递:实参和形参在内存上指向同一个地址
  • this关键词:代表当前的类或者当前这个对象

类与对象

  • 类:

    • 一种抽象的数据类型。对某一类事物的整体描述/定义,并不代表某一个具体的事物

    • Person类、Pet类等,描述/定义某一类具体事物所具备的特点

    • 一个类中,只存在两种内容

      public class Student{
          // 属性:字段
          String name;
          int age;
          
          // 方法
          public void study(){
              System.out.println(this.name+"is studying!");
          }
      }
      
  • 对象:

    • 抽象概念的具体实例
    • 张三是Person类的具体实例,旺财是Pet类的具体实例
    • 能够体现出特点,展现出功能的是具体的实例,而不是抽象的概念。因此需要将类实例化为对象
    • 引用类型:除了8个基本类型都是引用类型
      • 对象是通过引用来操作的:栈 -> 堆(里面保存地址)

创建与初始化对象

【规范】:在写程序时,不要在每个类中都写main方法。一个程序中只有一个main方法,可以定义一个大的启动类(可以叫Application类),并把它放到一边,便于我们随时启动测试

public class Application{
    public static void main(String[] args){
        // 类:抽象的,先实例化。实例化后会返回一个自己的对象
        // student对象就是一个Student类的具体实例
        // 这里的Student()就是构造方法
        Student student = new Student();
        // 这就叫:以对象的形式封装数据
        student.name = "xiaoming";
        student.age = 3;
    }
}
  • 使用new关键字创建对象
    • 用创建出的对象+"."来进行调用
    • 在使用new关键字时,除了分配内存空间之外,还会给创建好的对象,进行默认的初始化以及对类中构成器的调用
    • 类中的构成器,也叫做构造方法。是进行创建对象的时候必须要调用的(即使用new关键字,必须要有构造器,它的本质就是调用构造器)
      • 构成器的特点:
        1. 必须和类的名字相同
        2. 必须没有返回类型,也不能写void
      • 构成器的应用:
        1. 初始化一些信息
        2. 有参构造:一旦定义了有参构造,无参构造就必须显式定义(方法重载)
      • 快速生成构造器:alt + insert
public class Student{
    // 属性:字段Field 成员变量
    /* 
       初始值:
       数字:0 0.0
       char: u0000
       boolean: false
       引用:null
    */
    String name;
    int age;
    
    // 方法
    // 一个类即使什么也不写,也存在一个构造方法
    // 显式定义构造方法
    public Student(){
        this.name = "xiaoming";
    }
    // 有参构造
    publiv Student(String name){
        this.name = name;
    }
    public void study(){
        System.out.println(this.name+"is studying!");
}

内存分析

在这里插入图片描述

封装

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

  • 定义:就是数据的隐藏。通常应该禁止直接访问一个对象中的数据的实际表示,而应该通过操作接口来访问。

  • 做法:属性私有(private),get/set

  • 意义:

    • 提高程序的安全性,保护数据
    • 隐藏代码的实现细节
    • 统一接口(getXxx()、setXxx())
    • 系统可维护性增加
  • 【规范】

    // 启动类
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
            // 属性是私有(private)的无法直接用student.name这样的方式进行调用
            // 公有(public)的可以用student.name这样的方式进行调用
            // 【对于外部类来说,内部类依然可以调用】
            s s = new s();
            int name = s.name;
            student.setName("xiaoming");
            System.out.println(student.getName());
        }
    
        public static class s{
            private int name;
        }
    }
    
    public class Student {
        //属性私有
        private String name;
        private int age;
        private char gender;
        // 提供一些可以操作这个属性的方法:public的get和set方法。也就是接口方法
        // get方法,获取这个数据
        public String getName() {
            return name;
        }
    
        // set方法,给这个数据设置值
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            // 可以在set中加入条件判断
            if (age > 120 || age < 0){
                this.age = 3;
            }else{
                this.age = age;
            }
        }
    
        public char getGender() {
            return gender;
        }
    
        public void setGender(char gender) {
            this.gender = gender;
        }
    }
    
    

继承

  • 定义:本质是对某一批类的抽象,从而实现对现实世界更好的建模

    • 继承是类和类之间的一种关系。除此之外还有依赖、组合、聚合等
    • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends表示
    • 子类和父类之间,从意义上讲应该具有"is a"的关系
  • 方法:使用extends关键字。子类是父类的扩展

  • 功能:

    • 子类可以继承父类的方法和属性,拥有父类的全部公有(public)方法和属性【对于私有(private)的属性,可以使用get、set方法,封装的思想】
    // 学生  is  人: 派生类(父类),子类
    public class Person {
        
        public Person(){
             System.out.println("Person的无参构造");
        }
        
        public int money = 10;
        protected String name = "xiaoli";
        public void say(){}
        public void print(){
            System.out.println("person");
        }
    }
    public class Student extends Person{}
    public class Teacher extends Person{
        
        public Teacher(){
            super();  // 继承中的这一行代码被隐藏了。并且这一代码必须放在子类构造器的第一行
             System.out.println("Teacher的无参构造");
        }
        
        private String name = "xiaoming";
        
        public void print(){
            System.out.println("teacher");
        }
        
        public void test(String name){
            System.out.println(name);  // xiaofang 
            System.out.println(this.name);  // xiaoming 
            System.out.println(super.name);  // xiaoli
        }
        public void test1(){
            print();  // teacher
            this.print();  // teacher
            super.print();  // person
        }
    }
    public class Application {
        public static void main(String[] args) {
            Teacher teacher = new Teacher();  // 输出:Person的无参构造\nTeacher的无参构造
            teacher.say();
            teacher.test("xiaofang");
        }
    }
    
  • Java中只有单继承,没有多继承(一个儿子只能有一个爸爸,extends后只能有一个类,但是可以一个爸爸可以有多个儿子,多个类extends同一个类)

  • 在java中,所有类都默认继承object类

  • super,与this相对应,表示父类。只有存在继承才可以使用

    • 注意:

      1. super()调用父类的构造方法必须放在子类构造器或者子类方法的第一行

      2. this()和super()不能同时存在,因为都要求放在第一行

      3. 若父类中存在有参构造没有无参构造,则不能调用super(),并且要写上super(参数),否则会报错

方法重写

  • 定义:子类重写父类的方法

  • 要求:子类的方法与父类的方法必须要一致,但方法体不同

  • 方法:alt + insert -> override

  • 为什么需要重写:

    • 父类的功能,子类不一定需要,
    • 父类的功能不一定满足需求
  • 注意:

    • 重写只针对方法,与属性无关
    • 重写只针对非静态方法
      • 对于static方法,方法属于类,它不属于实例
      • 对于非static方法,子类的重写会重写父类的方法
      • 静态方法与非静态方法差别很大
    • 重写的修饰符:范围可以扩大,但不能缩小,eg:父类是private,子类是Default

    【修饰符的范围:public > Protected > Default > private】

    • 重写的抛出异常:范围可以被缩小,但不能扩大。eg: ClassNotFoundException --> Exception
    • final修饰的是常量,不能被改变

IDEA中,代码左边栏中的O代表重写(overwrite method)

public class B{
    public static void test(){
        System.out.println("B");
    }
    public static void testAgain(){
        System.out.println("B");
    }
}

public class A extends B{
    public static void test(){
        System.out.println("A");
    }
    // @Override表示重写
    @Override  // 注解:有功能的注释
    public static void testAgain(){
        System.out.println("A");
    }
    
}

public class Application{
    public static void main(String[] args){
        // 方法的调用只和左边定义的数据类型有关
        // 对于static方法,方法属于类,它不属于实例
        A a = new A();
        B b = new A();  
        // 我理解的是子类的构造器会首先运行父类的构造器,因此可以如此定义。
        // 并且对于静态方法,可以直接通过类来访问,因此输出结果为A
        a.test(); // A
        // 父类的引用指向子类
        b.test(); // B
        // 对于非static方法,子类的重写会重写父类的方法
        a.testAgain();  // A
        b.testAgain();  // A
    }
}

重写与重载的区别:

  • 重载是在同一个类中,方法名相同,参数列表不同
  • 重写需要有继承关系,子类重写父类的方法
  • 重写的方法名必须相同,参数列表也必须相同

多态

  • 定义:同一方法可以根据发送对象的不同而采用多种不同的行为方式
  • 一个对象的实际类型是确定的,但是它可以指向的引用类型不确定(即下方代码中的te1, te2, te3调用say方法时产生不一样的行为,这就是多态)
  • 应用场景:通过多态可以让代码的可扩展性变得更强,让代码更灵活
public class Person {
    public void say(){
        System.out.println("i am a person");
    }
}

public class Teacher extends Person{
    @Override
    public void say() {
        super.say();
        System.out.println("i am a teacher");
    }
}

public class Application {
    public static void main(String[] args) {
        Teacher te1 = new Teacher();
        // 父类的引用指向子类
        Person te2 = new Teacher();
        Object te3 = new Teacher();

        // 对象执行哪些方法,主要看对象左边的类型,和右边关系不大
        // 子类可以调用自己或者继承自父类的方法
        te2.say();  // 方法被重写
        te1.say();
        // 父类可以指向之类,但是不能调用子类独有的方法
        // te3.say();  // 报错,因为Object类中没有这一方法
        ((Teacher) te3).say();  // (Teacher) te3强制类型转换,可以由父类转换成子类(高到低)

    }
}
  • 条件:

    • 需要有继承关系
    • 方法需要被重写
    • 父类的引用指向子类对象 Father f1 = new Son()
  • 注意:

    • 多态是方法的多态,与属性无关
    • 父类和子类必须有联系,否则类型转换异常 ClassCastException
    • 不能被重写的方法(static final private)无法实现多态

instanceof

用于判断两个变量之间有无父子关系

public class Person {
    public void run(){
        System.out.println("run");
    }
}
public class Teacher extends Person{
    public void go(){
        System.out.println("go");
    }
}
public class Student extends Person{}

public class Application {
    // Object > String
    // Object > Person > Teacher
    // Object > Person > Student
    public static void main(String[] args) {
        Object te3 = new Teacher();
        System.out.println(te3 instanceof Teacher);  // true
        System.out.println(te3 instanceof Person);  // true
        System.out.println(te3 instanceof Object);  // true
        System.out.println(te3 instanceof Student);  // false
        System.out.println(te3 instanceof String);  // false
        
        Person te2 = new Teacher();
        System.out.println(te2 instanceof Teacher);  // true
        System.out.println(te2 instanceof Person);  // true
        System.out.println(te2 instanceof Object);  // true
        System.out.println(te2 instanceof Student);  // false
        // System.out.println(te2 instanceof String);  // 编译错误
        Teacher te1 = new Teacher();
        System.out.println(te1 instanceof Teacher);  // true
        System.out.println(te1 instanceof Person);  // true
        System.out.println(te1 instanceof Object);  // true
        // System.out.println(te1 instanceof Student);  // 编译错误
        // System.out.println(te1 instanceof String);  // 编译错误
        
        // 类型之间的转换
        // 基本类型转换:高转低需要强转,低转高则不需要
        // 类之间的转换(父类与子类):高转低需要强转,低转高则不需要(同理)
        // 高         低
        Person te = new Teacher();
        // 子类转父类可能会丢失一些方法
        // te.go();  // 报错,父类无法执行子类中的方法,因此要进行转换
		Teacher t = new (Teacher) te;
        t.go();
        ((Teacher) te).go();
    }
}

类转换的作用:方便方法的调用,减少重复的代码。简洁

static关键字

使用总结:

public class Application {
    {
        // 匿名代码块,在构造器之前执行,无法主动调用
    }
    static {
        // 静态代码块,在构造器之前执行,只执行一次(在匿名代码块之前执行)
        // 应用:赋初值
    }
    private static int age;
    private double score;

    // 非静态方法可以调用静态方法
    public void run(){
        go();
    }

    public static void go(){
        // run();  // 报错
    }
    public static void main(String[] args) {
        Application stu = new Application();

        System.out.println(stu.age);
        System.out.println(stu.score);
        System.out.println(Application.age);
        // System.out.println(Application.score);  // 报错

        stu.run();
        stu.go();
        // Application.run();  // 报错
        Application.go();
        go();
        // run();  // 报错
    }
}

其他应用:

// 静态导入包
import static java.lang.Math;
// import static java.lang.Math.random;
public class Application {
    public static void main(String[] args) {
        // System.out.println(Math.random());
        // 通过静态导入包来进行替代写法
        System.out.println(random());
    }
}

final修饰类

不能被继承(inherent)

public final class Person{}
public class Student extends Person{}  // 报错

抽象类

  • abstract修饰符。

    • 用来修饰类,就是抽象类;
    • 用来修饰方法,则为抽象方法
  • 抽象类中可以没有抽象方法,但是存在抽象方法的类一定要声明为抽象类

  • 抽象类,不能使用new关键字来创建对象,它的应用场景在于被子类继承

  • 抽象方法,只有方法的声明,没有方法的实现,它的应用场景在于被子类实现

  • 子类继承抽象类,那么就被需要实现抽象类中没有实现的抽象方法,否则该子类也要被声明为抽象类

  • 抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。在继承了抽象类的子类中通过super()或super(参数列表)调用抽象类中的构造方法。

  • 应用:为后续的操作做一个规范,把后面的一些公有属性抽象出来,避免反复进行操作。总之,就是为了提高开发效率,提高可扩展性

【理解:抽象类就是提供一个框架,让你后续的操作沿着框架进行】

public abstract class Person {
    // 作为一个约束,让别人帮助实现
    public abstract void doSomething();
    public void done(){}
}
public class Teacher extends Person{
    @Override
    public void doSomething() {}
}

接口(最能体现OO精髓)

  • 存在的意义:类只能单继承,因此有一定的局限性,为了克服这种局限性,使用多继承的接口

  • 定义:提供一种规范,类似于现实世界中”如果你是……你必须……“的思想。

  • 本质:一种契约,类似法律,大家都要遵守并执行

  • 普通类、抽象类、接口的区别

    • 普通类:只有具体实现(普通方法)
    • 抽象类:既存在规范(抽象方法)又存在具体实现
    • 接口:仅有规范!【约束和实现分离】
  • 声明接口:用关键字interface

public interface Person {
    // 作为一个约束,让别人帮助实现,接口必须有实现类
    void doSomething();  // 默认隐藏public abstract
    void go(String name);
}

public interface Student{
    // 一般不定义常量
    String name = "xiaoming";  // 常量默认隐藏public static final
    void cry();
}

// 类通过implements实现接口
public class Teacher implements Person,Student{
    @Override
    public void doSomething() {}

    @Override
    public void go(String name) {

    }

    @Override
    public void cry() {

    }
}
  • 作用:
    • 作为约束,提供一个架构(架构师)
    • 定义一些方法,让不同的人可以通过不同思路实现
    • 接口不能被实例化(接口中不存在构造方法)
    • 可以实现多个接口(多继承)
    • 必须重写接口中的方法

内部类(扩展,先来见一下)

  • 定义:在一个类的内部定义另一个类
  • 包括:
    • 成员内部类
    • 静态内部类
    • 局部内部类
    • 匿名内部类
    • lambda表达式
public class Out {
    private int id;
    public void out(){
        System.out.println("this is out");
        // 局部内部类: 在方法中
        class inner2{}
    }

    class Inner1{
        public void in(){
            System.out.println("this is in");
        }

        // 成员内部类
        // 获取外部类的私有属性和方法
        // 同样的,若内部类为static(静态内部类)则无法获得外部类的属性和方法
        public void getID(){
            out();
            System.out.println(id);
        }
    }
}
// 一个java中可以有多个class,但只能有一个public class
class A{
}

public class Application {
    public static void main(String[] args) {
        Out out = new Out();
        // 通过这个外部类来实例化内部类
        Out.Inner1 inner = out.new Inner1();
        inner.in();
        inner.getID();
        // 匿名内部类
        new Apple().eat();
        Service hello = new Service() {
            @Override
            public void hi() {
                System.out.println("hello");
            }
        };
    }
}
class Apple{
    public void eat(){
        System.out.println("good");
    }
}

interface Service{
    void hi();
}
这篇关于面向对象编程(OOP)【感谢狂神说JAVA】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!