面向对象编程

面向对象编程(OOP)

什么是面向对象?

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

  • 类=属性+方法

  • 面向对象编程的本质就是:以类的方式组织代码,以对象的方式组织(封装)数据

  • 抽象

  • 三大特性:

    • 封装
    • 继承
    • 多态
  • 从认识论的角度:现有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。

  • 从代码运行的角度:现有类后有对象。类是对象的模板。


回顾方法及加深

  • 方法的定义:
    • 修饰符(例如public, static等...)
    • 返回值类型
    • break(跳出switch,结束循环)和return的区别
    • 方法名:注意命名规范(首字母小写+驼峰原则);见名知意
    • 参数列表:(参数类型 参数名)...
    • 异常抛出

Demo:

package com.judy.oop;
//Demo01类
public class Demo01 {
    //main方法
    public static void main(String[] args) {
        System.out.println(sayHello());
        System.out.println(max(1,2));
    }
    /*
    修饰符 返回值类型   方法名(...参数列表){
        //方法体
        return 返回值;
    }
     */
    //return 结束方法,返回一个结果(结果可能为空)
    public static String sayHello() {
        return "hello";
    }
    public static int max(int a, int b) {
        return a>b?a:b;  //三元运算符
    }

}
  • 方法的调用:

    • 静态方法和非静态方法

    image-20251212200553830

    • 形参和实参

      package com.oop.demo01;
      
      public class Demo03 {
          public static void main(String[] args) {
              //实际参数和形式参数和类型要对应!
              int add=Demo03.add(1,2);
              System.out.println(add);
          }
          public static int add(int a, int b){
              return a+b;
          }
      }
      
    • 值传递和引用传递(java本质上都是值传递)

      image-20251212205729723

    • this关键字


类与对象的创建

  • 类与对象的关系:

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

    • 对象是抽象概念的具体实例

    • Demo:

      package com.oop.demo02;
      //学生类
      public class Student {
          //属性:字段
          String name; //null
          int age;     //0
      
          //方法
          public void study(){
              System.out.println(this.name+"在学习");
          }
      }
      
      /*
          public static void main(String[] args) {
              //类:抽象的,需要实例化
              //类实例化后会返回一个自己的对象!
              //student对象就是一个Student类的具体实例!
              Student student = new Student();
      
              student.name = "judy";
              student.age = 23;
      
              System.out.println(student.name);
              System.out.println(student.age);
          }
       */
      
  • 创建与初始化类:

    • 使用new关键字创建对象

    • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化,以及调用类中的构造器

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

      1.必须和类的名字相同; 2.必须没有返回类型,也不能写void

    • Demo:

      package com.oop.demo02;
      //java → class
      public class Person {
          String name;
          //一个类即使什么都不写,编译生成class文件后,也会存在一个默认的无参构造器
      
          //显示的定义构造器
          //1.无参构造器:
          public Person() {
              this.name = "jhz";
          }
          //2.有参构造器:一旦定义了有参构造器,无参构造器就必须显示定义!
          public Person(String name) {
              this.name = name;
          }
      }
      
      /*
          public static void main(String[] args) {
              //使用new关键字实例化了一个对象
              Person person = new Person("judy");
              System.out.println(person.name);
          }
      
          总结:
          1.构造器:
          (1)与类名相同
          (2)没有返回值
          2.作用:
          (1)new本质在调用构造器
          (2)用于初始化对象的值
          3.注意点:
          (1)定义有参构造器之后,如果想使用无参构造器,必须显式定义一个无参构造器!
          (2)IDEA快捷键:Alt+(Fn)+Insert,选择"Constructor"
       */
      

    构造器必须掌握!

  • 创建对象内存分析:

    image-20251213163937875

    • :方法;引用变量名(会存放这个引用在堆里面的具体地址,对象是通过引用来操作的);存放基本类型的变量(会包含这个基本类型的具体数值)
    • :存放具体new创建出来的对象(和数组),可以被所有的线程共享
    • 方法区:包含所有的class和static变量,可以被所有的线程共享

Java的三大特征

(一)封装:

  • 该露的露,该藏的藏

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

    • 高内聚:类的内部数据操作细节自己完成
    • 低耦合:仅暴露少量的方法给外部使用
  • 封装就是数据的隐藏:

    通常,应该禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,这称为信息隐藏

  • 总结:属性私有,get/set

  • Demo:

    package com.oop.demo04;
    //类
    public class Student {
        //private关键字:私有
        private String name; //名字
        private int age;      //年龄
        private int id;      //学号
        private char sex;    //性别
    
        //封装大都是对于属性而言,方法一般不需要封装
        //学习(),睡觉()
    
        //提供一些可以操作这些属性的方法!(public的get/set方法)
        //get获得这个数据
        public String getName() {
            return this.name;  //this关键字是可选的(当前没有与成员变量同名的,局部变量或参数)
        }
        //set给这个数据设置值
        public void setName(String name) {
            this.name = name;
        }
    
        //Alt+(Fn)+Insert可以自动生产get,set方法
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public char getSex() {
            return sex;
        }
    
        public void setSex(char sex) {
            this.sex = sex;
        }
    
        public int getAge() {
            return age;
        }
        //set中添加安全性判断,可以提高程序的安全性
        public void setAge(int age) {
            if (age < 0 || age > 100) {
                System.out.println("年龄不合法!");
            }else{
                this.age = age;
            }
        }
    }
    
    package com.oop.demo04;
    /*
        封装的作用:
        1.提高程序的安全性(set中添加安全性判断),保护数据
        2.隐藏代码的实现细节
        3.统一接口
        4.增加系统的可维护性
     */
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
            //student.name不能得到属性值
    
            String name = student.getName();
            System.out.println(name); //null
    
            student.setName("judy");
            System.out.println(student.getName()); //judy
            System.out.println(name); //null
    
            student.setAge(18);  //如果设置的年龄不合法,则会输出“年龄不合法”
            System.out.println(student.getAge());
        }
    }
    

(二)继承

  • 继承的本质是对某一批类的抽象

  • extends的意思是“扩展”,子类是父类的扩展

  • Java中类只有单继承,没有多继承!

  • 继承是类和类之间的一种关系。

    • 除此之外,类和类之间的关系还有依赖、组合、聚合等
    • 继承关系的两个类,一个是父类(基类),一个是子类(派生类)。
    • 子类和父类之间,从意义上讲应该具有"is a"的关系
  • Demo:

    package com.oop.demo05;
    //在Java中,所有的类,都默认直接或间接继承Object类
    //人:父类
    public class Person /*extands Object*/{
        //关键字优先级:public, protected, default, private
        //一般父类的方法是public的(子类可继承),属性是private的
        private int money=10_0000_0000;
    
        public void say(){
            System.out.println("说了一句话");
        }
    
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    }
    
    package com.oop.demo05;
    //Teacher is a Person: 派生类/子类
    public class Teacher extends Person {
    }
    
    package com.oop.demo05;
    //Student is a Person: 派生类/子类
    //子类继承了父类,就会拥有父类的全部public方法!(属性一般是private)
    public class Student extends Person {
    }
    
  • Ctrl+H:可以查看与当前类相关的的层级结构

    image-20251213200647950

  • super关键字注意点:

    • 通过super()调用父类的构造方法,必须写在构造方法的第一行
    • super必须只能出现在子类的方法或者构造方法中
    • super和this不能同时调用构造方法(因为这两者都要求写在第一行)
  • 方法重写:(为什么需要重写:父类的功能,子类不一定需要)

    需要有继承关系,子类重写父类的方法。重写都是方法的重写,和属性无关!

    • 方法名必须相同

    • 参数列表必须相同

    • 方法体不同

    • 修饰符,子类重写后的方法范围可以比父类扩大但不能缩小:puclic > protected > default > private

    • 抛出的异常,范围可以缩小但不能扩大:Exception > ClassNotFoundException

    • Alt+Fn+Insert,选择override

    • Demo:

      package com.oop;
      
      import com.oop.demo05.A;
      import com.oop.demo05.B;
      
      public class Application {
          public static void main(String[] args) {
              //静态方法与非静态方法的区别很大!
              //1.静态方法:方法的调用只与定义的数据类型(左边)有关,不算方法重写!
              A a = new A();
              a.staTest();  //调用的A类的静态方法,A=>staTest()
      
              //父类(B)的引用b指向了子类(A)
              B b = new A();
              b.staTest();  //调用的B类的静态方法,B=>staTest()
      
              //2.非静态方法:重写
              a.test();   //A=>test()
              b.test();   //A=>test()
          }
      }
      
      package com.oop.demo05;
      
      public class A extends B {
          public static void staTest() {
              System.out.println("A=>staTest()");
          }
      
          //Override重写
          @Override //注解:有功能的注释!
          public void test() {
              System.out.println("A=>test()");
          }
      }
      
      package com.oop.demo05;
      
      public class B {
          public static void staTest() {
              System.out.println("B=>staTest()");
          }
          public void test() {
              System.out.println("B=>test()");
          }
      }
      

(三)多态

  • 同一个对象,在不同时刻表现出来的不同形态(即同一方法可以根据发送对象的不同而采用多种不同的行为方式)

    Cat cat = new Cat();
    Animal animal= new Cat();
    
  • 一个对象的实际类型是确定的,但指向对象的引用的类型可以有多个(父类)

  • 多态存在的条件:

    • 有继承关系
    • 子类重写父类的方法
    • 父类引用指向子类对象:Father f1=new Son();
  • 注意:多态是方法的多态,属性没有多态性!(animal的成员变量只有父类的属性)

  • Demo:

    package com.oop;
    
    import com.oop.demo06.Student;
        import com.oop.demo06.Person;
    
    public class Application {
        public static void main(String[] args) {
            //一个对象的实际类型是确定的!
            //new Student()
            //new Person()
    
            //可以指向的引用类型就不确定了:父类的引用可以指向子类
            Student s1 = new Student();
            Person s2 = new Student();
            Object s3 = new Student();
    
            s1.run();  //子类重写了父类的方法,则会执行子类的方法
            s2.run();
    
            //对象能执行哪些方法,主要看对象定义的类型(左边)!
            //子类能调用自己的、或继承自父类的方法;父类的引用可以指向子类的实例,但不能调用子类独有的方法
            s1.eat();
            ((Student)s2).eat();
            //s2.eat();不能执行,因为Person类里没有eat()方法
        }
    }
    
    package com.oop.demo06;
    
    public class Person {
        public void run(){
            System.out.println("run");
        }
    }
    
    package com.oop.demo06;
    
    public class Student extends Person {
        @Override
        public void run() {
            System.out.println("student run");
        }
    
        public void eat() {
            System.out.println("student eat");
        }
    }
    
  • instanceof:1.判断两个类之间是否存在父子关系;2.准备进行引用类型转换

    • Demo:

      package com.oop;
      
      import com.oop.demo06.Student;
      import com.oop.demo06.Person;
      import com.oop.demo06.Teacher;
      
      public class Application {
          public static void main(String[] args) {
              //Object > Person > Student
              //Object > Person > Teacher
              //Object > String
              //System.out.println(X instanceof Y); //能不能编译通过,取决于X与Y是否存在线性的父子关系
      
              Object object = new Student();
      
              System.out.println(object instanceof Student);  //true
              System.out.println(object instanceof Person);   //true
              System.out.println(object instanceof Object);   //true
              System.out.println(object instanceof Teacher);  //false
              System.out.println(object instanceof String);   //false
              System.out.println("===================================");
      
              Person person = new Student();
      
              System.out.println(person instanceof Student);  //true
              System.out.println(person instanceof Person);   //true
              System.out.println(person instanceof Object);   //true
              System.out.println(person instanceof Teacher);  //false
              //System.out.println(person instanceof String);  //编译报错!Person类和String类是同级的,根本没关系
              System.out.println("===================================");
      
              Student student = new Student();
      
              System.out.println(student instanceof Student);  //true
              System.out.println(student instanceof Person);   //true
              System.out.println(student instanceof Object);   //true
              //System.out.println(student instanceof Teacher); //编译报错!Student类和Teacher类是同级的,根本没关系
              //System.out.println(student instanceof String);  //编译报错!Student类和String类根本没关系
      
              //引用类型之间的转换:父>子
              //1.低 → 高,自动转换(子类转换为父类,可能丢失自己本来的一些方法!)
              Person p = new Student();
              //p.go();    无法调用Student类的go方法
      
              //2.高→ 低,需要强制转换成子类
              Student p1 = (Student) p;
              p1.go();
      
          }
      }
      
      package com.oop.demo06;
      
      public class Person {
          public void run(){
              System.out.println("run");
          }
      }
      
      package com.oop.demo06;
      
      public class Student extends Person {
          public void go(){
              System.out.println("go");
          }
      }
      
      package com.oop.demo06;
      
      public class Teacher extends Person {
      
      }
      
  • 补充知识:

    • static关键字详解

      • 静态变量/非静态变量
      • 静态方法/非静态方法
      package com.oop.demo07;
      
      //static
      public class Student {
          private static int age;  //静态变量/类变量(被类中的实例共享,建议使用“类名.变量名”访问)
          private double score;    //非静态变量
      
          public void run(){
              System.out.println("run");
              go();  //在同一个类里面,非静态方法可以直接调用静态方法
          }
      
          public static void go(){
              System.out.println("go");
          }
      
          public static void main(String[] args) {
              Student s = new Student();
      
              System.out.println(Student.age);
              //System.out.println(Student.score); 非静态变量不能通过类名.访问
              System.out.println(s.age);
              System.out.println(s.score);
      
              // run();不能直接运行,必须通过new出来的对象.访问
              new Student().run();
      
              go(); //在同一个类里面,静态方法可以直接调用静态方法,但是不能直接调用非静态方法(需创建对象.)
          }
      }
      

image

  • 代码块:

    • 匿名代码块
    • 静态代码块
    package com.oop.demo07;
    
    public class Person {
        //2:赋初始值
        {
            //代码块(匿名代码块)
            System.out.println("匿名代码块");
        }
    
        //1:第一个执行(和类一起加载)只执行一次
        static{
            //静态代码块
            System.out.println("静态代码块");
        }
    
        //3.
        public Person() {
            //构造器
            System.out.println("构造方法");
        }
    
        public static void main(String[] args) {
            Person p1 = new Person();
            System.out.println("===============");
            //每创建一个对象,就会先走匿名代码块,在执行构造函数
            Person p2 = new Person();
        }
        /*
        output:
            静态代码块
            匿名代码块
            构造方法
            ===============
            匿名代码块
            构造方法
         */
    }
    
  • final修饰符:final修饰的类不可以被继承,没有子类!

  • 静态导入包:

    package com.oop.demo07;
    //静态导入包:
    import static java.lang.Math.random;
    import static java.lang.Math.PI;
    
    public class Test {
        public static void main(String[] args) {
            //System.out.println(Math.random());
            //静态导入包之后可以直接调用random()
            System.out.println(random());
            System.out.println(PI);
        }
    }
    

抽象类和接口

(一)抽象类

  • abstract修饰符可以修饰方法也可以修饰类。(抽象方法/抽象类)

  • 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类!

    • 抽象类,不能使用new关键字创建对象,它是用来让子类继承的
    • 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
  • 子类继承抽象类,那么就必须实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类

  • Demo: 抽象类是一种”约束“、”规范“

    package com.oop.demo08;
    
    //抽象类:extends类都是单继承(接口可以多继承)
    public abstract class Action {
        //抽象方法:只有方法名,没有方法的实现
        public abstract void doSomething();
    }
    
    package com.oop.demo08;
    
    //抽象类的所有方法,继承它的子类都必须实现(除非这个子类本身也是abstract抽象类)
    public class A extends Action{
        @Override
        public void doSomething() {
        }
    }
    
  • 思考题:

    • 抽象类存在构造器;
    • 存在读意义:提高代码开发效率和可扩展性

(二)接口(interface)

  • 对比:

    • 普通类:只有具体的实现
    • 抽象类:具体的实现和规范(抽象方法)都有!
    • 接口:只有规范!(约束和实现分离)
  • 接口就是规范,定义的是一组规则

  • 接口的本质是契约

  • Demo:

    package com.oop.demo09;
    
    //interface关键字定义的是接口,接口都需要有实现类
    public interface UserService {
        //接口中的属性都是常量(public static final),但一般不在接口中定义常量!
        //public static final int AGE=99;
        int AGE=99;
    
        //接口中的所有定义的方法,其实都是public abstract的
        //public abstract void run();
        void add(String name);
        void delete(String name);
        void update(String name);
        void querye(String name);
    }
    
    package com.oop.demo09;
    
    public interface TimeService {
        void timmer();
    }
    
    package com.oop.demo09;
    
    //接口的实现类名字一般都以"Impl"结尾
    //抽象类的继承:extends只有单继承
    //类可以通过"implements接口"实现接口;实现了接口的类,就必须重写接口中的定义的方法!
    //多继承:通过接口间接实现多继承
    public class UserServiceImpl implements UserService,TimeService {
        @Override
        public void add(String name) {
    
        }
    
        @Override
        public void update(String name) {
    
        }
    
        @Override
        public void delete(String name) {
    
        }
    
        @Override
        public void querye(String name) {
    
        }
    
        @Override
        public void timmer() {
    
        }
    }
    
  • 总结:

    1. 接口就是约束(定义了一些方法,让不同的人实现)

    2. 接口中的所有定义的方法,都是public abstract的

    3. 接口中的属性都是常量(public static final),但一般不在接口中定义常量!

    4. 接口中没有构造器,接口不能被实例化

    5. 通过implements关键字可以实现多个接口(extends则只能单继承类)

    6. 必须要重写接口中的方法


内部类

  • 内部类就是在一个类的内部再定义一个类(比如,在A类中定义一个B类,则B类就是A的内部类,而A类是B类的外部类)

  • 分类:

    • 成员内部类

      package com.oop.demo10;
      
      public class Outer {
          private int id=10;
          private void out(){
              System.out.println("这是外部类的方法");
          }
      
          public class Inner {
              public void in(){
                  System.out.println("这是内部类的方法");
              }
      
              //内部类可以获取外部类的私有属性和私有方法!(public的就更能获取了)
              public void getid(){
                  System.out.println(id);
                  out();
              }
          }
      }
      
      package com.oop;
      
      import com.oop.demo10.Outer;
      
      
      public class Application {
          public static void main(String[] args) {
              Outer outer= new Outer();
      
              //通过外部类来实例化内部类
              Outer.Inner inner=outer.new Inner();
              inner.in();
              inner.getid();
      
          }
      }
      
    • 静态内部类:static的内部类不能访问外部类中非static的属性

    • 局部内部类:写在方法里面的类

      image-20251215204954725

    • 匿名内部类:没有类名,只使用一次,在定义的同时被实例化
      通常用于需要临时:1.实现某个接口-最常用;2.继承普通类并重写方法;3.继承抽象类并实现抽象方法

  • 一个 Java 源文件中可以有多个类(class),但最多只能有一个 public类,且这个public class的名称必须与源文件名相同。

image-20251215204928339

posted @ 2025-12-11 21:39  juuddy  阅读(12)  评论(0)    收藏  举报