java基础(三):java面向对象OOP

java面向对象OOP

基本概念

  1. 面向过程与面向对象

    面向过程:关注代码实现的细节、复用性

    面向对象:先把每个过程的代码实现细节整合到对象中,只要找到对象就能拥有对象身上所有的功能。

    面向对象基于面向过程

  2. 类与对象

    对一类对象进行抽取,对共有的特征抽取成属性,共有的行为抽取成方法,这一类对象抽取成类。 — 类就是对象的概括,对象是类的具体实现

    在java中所有非静态的属性和方法都要通过对象调用

    万物皆对象

  3. 构造方法

    • 与类同名,没有返回值类型

    • 如果类中没有定义任何形式的构造方法,JVM会在底层默认添加一个无参构造

    • 构造方法支持重载,有参构造可以进行属性初始化

    • 如果类中定义任何形式的构造方法,JVM不会再添加无参构造

    • 类中最少会有一个构造方法

  4. this指针

    • 关键字:代表当前类的对象

    • this可以代表类还没有产生对象,可以代表类刚创建的对象,也可以代表正在使用的对象

    • this不是真实的对象,是一个虚拟的指代,指代地址值

    • this的主要功能是在本类的构造方法中调用其他形式的构造方法,this语句一定要在构造方法的首行

    示例:

    class Person {
        // 特征 --- 属性
        String name;
        char gender;
        int age;
    
        // 如果一个类中没有定义构造方法,JVM在底层会默认定义一个无参构造
        // 构造方法特点: 1.与类同名 2. 没有返回值类型
        public Person(){
            System.out.println("this:" + this);  
            // this:cn.zss.Person@15aeb7ab
        }
    
        // 支持重载,有参构造
        // 如果类中定义了任意一个构造方法,JVM不会再添加
        public Person(String name){
            // this (关键字)
            // 代表当前类的对象
            this.name = name;
            System.out.println("A");
            System.out.println("this:" + this);
        }
    
        public Person(String name, int age, char gender){
            // this 语句在本类中的构造方法中调用其他形式的构造方法
            // this 语句一定要在首行
            this(name);
            this.gender = gender;
            this.age = age;
            System.out.println("B");
        }
    
        // 行为 --- 方法
        public void eat(){
            // this可以代表类还没有产生对象
            System.out.println(this.name + " eat too much");
            System.out.println("this:" + this);  
            // this:cn.zss.Person@15aeb7ab
        }
    }
        
    public class Demo{
    
        public static void add (int num){
            num += 1;
        }
    
        // 只要参数是引用类型只能接受对象
        public static void add (Person p){
            // new Person() 形参接受Person类对象
            p.age ++;
        }
    
        public static void main(String[] args) {
            // 构造方法,创建对象
            Person p = new Person();
            p.age = 19;
            p.name = "lili";
            p.eat();  // lili eat too much
    
            // 实参传入基本数据类型
            System.out.println(p.age);  // 19
            add(p.age);
            System.out.println(p.age);  // 19
            // 实参传入引用数据类型
            add(p);
            System.out.println(p.age);  // 20
    
            System.out.println("--------------------------------------");
            Person p2 = new Person("Poter",18,'男');
        }
    }
    
  5. 构造代码块

    • 在类内方法外的{}内
    • 用于属性初始化
    • 优先于任意形式的构造方法执行

    示例:

    class Baby {
        // 属性
        String name;
        char gender;
        int age;
    
        // 构造代码块
        // 会优先于所有的构造方法先执行,与位置无关
        {
            this.name = "Poter";
            this.gender = '男';
        }
    
        // 无参构造
        public Baby(){
            System.out.println("无参构造方法");
        }
    
        public Baby(String name, int age){
            this.name = name;
            this.age = age;
            System.out.println("有参构造方法");
        }
    }
    
    public class Demo{
        public static void main(String[] args) {
            Baby a = new Baby();
            System.out.println(a.name+","+a.gender);
            Baby b = new Baby("xiaoming",1);
            System.out.println(b.name+","+b.gender+","+b.age);
        }
    }
    
  6. 局部代码块

    • 在方法内的{}
    • 控制变量的生命周期,提高内存的利用率

    示例:

    public class Demo{
    	public static void main(String[]args){
    		int x = 1;
    		//局部(方法)代码块
    		//改变变量的生命周期
    		{
    			int y = 2;
    			System.out.println(x+y);
    		}		
    	}
    }
    
  7. 局部变量与成员变量

    a. 位置

    • 成员变量:类内方法外

    • 局部变量:方法内

    b. 使用范围

    • 成员变量:整个类

    • 局部变量:方法内

    c. 内存

    • 成员变量:堆
    • 局部变量:栈

    d. 生命周期

    • 成员变量:随着类创建的对象而产生,随着对象被系统回收时才消失
    • 局部变量:方法被调用执行时才产生,随着方法的调用结束才消失

面向对象的特性

  • 封装、继承、多态(抽象)

封装

  1. 类,方法 — 体现了封装的复用性
  2. 属性私有化 — 类中的属性进行私有化,提供公共的访问方式(方法)用于间接的操作私有化属性,提高代码的数据安全性

示例:

public class ObjectDemo {
    public static void main(String[] args) {
        Person p = new Person("lili",18,'女');
        p.setAge(20);
        System.out.println(p.getAge());
    }
}
class Person{
    // 私有化属性只能在本类中直接使用
    private String name;
    private char gender;
    private int age;

    public Person(String name, int age, char gender){
        // 对参数进行判断
        this.name = name;
        this.gender = gender;
        if (age >=0 && age <= 120){
            this.age = age;
        }
    }
    
    // 自己定义的getter 和 setter 方法
    // 提供方法间接给属性赋值
    public void setAge(int age){
        if (age >=0 && age <= 120){
            this.age = age;
        }
        else{
            System.out.println();
        }
    }

    // 提供方法间接的获取属性值
    public int getAge(){
        return this.age;
    }

    // java 可以提供自动提供的get方法和set方法
    // 右键 点击 Generator --- 选择Getter and Setter

}

继承

  • 如果多个类的内容出现重复,就要把重复的内容放到新类中,原来的类和新类之间通过extends关键字产生关联关系—继承。原来的类叫子类(派生类),新的类叫父类(超类)。子类可以继承父类的部分信息

  • 继承可以提高代码的复用性

  • 继承的方式(单继承 树状结构):类只能有一个父类,但可以有多个子类

  • 方法的重写:在父子类中出现方法签名一致的方法

    • 重写原则:两等两小一大

      • 两等:方法签名一致;如果父类的方法返回值类型是基本数据类型或者void, final,那么子类的返回值类型就和父类完全一致

      • 两小:如果父类的方法返回值类型是引用类型,那么子类的方法返回值类型要么和父类的方法返回值类型一致,要么是父类方法返回值类型的子类;子类抛出异常小于等于父类异常类型

        class A{}
        class B extends A{}
        class C{
        	public A m(){  // 返回值类型为A
                return null;
            }
        }
        class D extends C{
        	public B m(){  // 返回值类型为A的子类
                return null;
            }
        }
        
      • 访问权限修饰符

        关系:本类,子类,同包类,其他类

        本类子类同包类其他类
        public可以可以可以可以
        protected可以可以可以不可以
        默认可以同包子类可以不可以
        private可以不可以不可以不可以

        **一大:**子类的方法访问权限修饰符要么和父类的方法访问权限修饰符一致,要么比父类的范围大

        class EDemo{
            // 类的私有化,对子类不可见
            private void m(){ }
            void n(){}  // 默认
        }
        
        class EDemo1 extends EDemo{
            public void m(){ } // 不是重写,相当于定义一个新方法
            public void n(){}  // 重写,public范围大于默认范围
        }
        

        **父类私有化信息、构造方法、构造代码块都不能被子类继承 **

        public class ExtendDemo2 {
        }
        
        class EDemo{
            // 类的私有化,对子类不可见
            private void m(){ }
            void n(){}  // 默认
        }
        
        class EDemo1 extends EDemo{
            public void m(){ } // 不是重写,相当于定义一个新方法
            public void n(){}  // 重写
        }
        

        注意: 如果protected修饰的信息和要获取的位置关系是其他类位置,保证子类对象只能在本类中使用才能调用protected修饰的信息

        /*定义packet cn.zss.a中的类A*/
        package cn.zss.a;
        public class A {
            protected void m(){}
        }
        
        /*定义packet cn.zss.b中的类B和C*/
        package cn.ysu.b;
        
        import cn.ysu.a.A;  // 导入类A的包
        
        public class B extends A{
        
            public static void main(String[] args) {
                // 创建对象
                A a = new A();
                B b = new B();
                // a对象和m()所在的位置是其他类
                // a.m();  // error 无法访问protected修饰的方法
        
                b.m();  // correct
                // b对象和m()所在位置是其他类,B类继承A类
            }
        }
        
        class C extends A{
            public void n(){
                C c = new C();
                c.m();
                // c对象和m()所在位置是其他类,C类继承A类
        
                B b = new B();
                // b.m();  // error
                // 如果protected修饰的信息和要获取的位置关系是其他类位置
                // 保证子类对象只能在本类中使用才能调用protected修饰的信息
            }
        }
        
  • super

    • 关键字,代表父类的对象,可以调用父类的属性和方法

    • super语句—在子类构造方法中调用父类的构造方法

      • 子类**所有形式*的构造方法默认都会通过super()调用父类的无参构造
      • 如果父类没有提供无参构造,子类所有的构造方法需要通过super语句强制调用父类对应形式有参构造
      • super 语句需要在首行 —和this冲突不能同时存在,但是this可以和super()的默认形式(不手动调用)同时存在
    • 父类对象优先于子类对象先存在

      • 父子类执行顺序(父类构造代码块-父类构造方法-子类构造代码块-子类构造方法
    public class ExtendsDemo2 {
        public static void main(String[] args) {
            Pig p = new Pig("lili");
            p.eat();
        }
    }
    
    class Animal{
    
        // 父类对象优先于子类对象先存在
    //    public Animal(){
    //        System.out.println("父类的无参构造");  // 1
    //    }
    //
        public Animal(String name){
            System.out.println("父类的有参构造");
        }
    
        // 方法
        public void eat(){
            System.out.println("Animals are eating");
        }
        public void sleep(){
            System.out.println("zzz...");
        }
    }
    
    class Pig extends Animal{
    
        // 父类对象优先于子类对象先存在
        // 若父类没有无参构造需要手动调用父类的有参构造
        // super 语句需要在首行
        public Pig() {
            super("lili");  // 手动调用父类有参构造,否则报错
            // supper语句 --- 在子类的构造方法中调用父类的构造方法
            // super(); 默认
            System.out.println("子类的无参构造");  // 2
        }
    
        // 子类所有形式的构造方法都会默认先调用父类的无参构造
        public Pig(String name){
            super("lili");  // 手动调用父类有参构造,否则报错
            System.out.println("子类有参构造函数");
        }
    
        // 重写方法
        public void eat() {
            System.out.println("The pig is eating");
    
            // 在java中所有非静态属性和方法都需要对象调用
            // this -代表当前类的对象
            // supper - 代表父类的对象
            sleep();  // 相当于 super.sleep();
        }
    }
    

多态

  • 在代码执行过程中可以呈现的多种形式
  1. 编译时多态 — 在编译时期绑定代码

    • 体现:重载
  2. 运行时多态 — 在运行时期绑定代码

    • 体现:重写,向上造型(在执行时才能知道对象调用的方法)

    • 前提:继承

    • 向上造型

      • 声明类是父类,实际创建的类是子类
      • 编译看左边引用的类型,运行看具体的对象类型
      • 向上造型的对象调用方法,可以调用哪些方法看父类,方法的具体执行看子类是否重写父类的方法,如果有重写调用子类方法,否则调用父类方法(父类 — 目录 子类 — 正文)
    • 优点:

      • 统一参数类型
      • 解耦 (降低耦合度 高内聚、低耦合)
    • 示例:

      public class DTDemo {
          public static void main(String[] args) {
              // Pet p;
              // p = new Dog();
              Pet p = new Dog();
              // 声明类是父类,实际创建类是子类
              // 向上造型
      
              // 向上造型的对象调用方法
              // 可以调用哪些方法看父类
              // 方法的具体执行看子类是否重写父类的方法
              // 如果有重写调用子类方法,否则调用父类方法
              p.eat();  // Dogs like to eat bones
              p.sleep();  // zzz...
              // p.bark();  // error,父类中没有,无法调用
      
              // 调用方法
              // 匿名对象 --- 当做参数使用
              System.out.println("-------------------------");
              petsEat (new Pet());  // Pets need to eat
              petsEat (new Cat());  // 向上造型  Cats like to eat fish
              petsEat (new Dog());  // 向上造型  Dogs like to eat bones
          }
      
          public static void petsEat(Pet p){  // 统一参数类型
              p.eat();
          }
      }
      
      class Pet{
          // 方法
          public void eat(){
              System.out.println("Pets need to eat");
          }
          public void sleep(){
              System.out.println("zzz...");
          }
      }
      
      class Dog extends Pet{
          @Override
          public void eat() {
              // super.eat();
              System.out.println("Dogs like to eat bones");
          }
      
          public void bark(){
              System.out.println("Wang Wang barking");
          }
      }
      
      class Cat extends Pet{
          @Override
          public void eat() {
              // super.eat();
              System.out.println("Cats like to eat fish");
          }
      
          public void CatchMice(){
              System.out.println("Cats can catch mice");
          }
      }
      
    • 解析重写原则

      1. 子类的方法访问权限修饰符要么和父类方法访问权限修饰符一致,要么比父类的范围大

        class A{
            public void fun(){}
        }
        class B extends A{
            void fun(){}  // 反证,证明这是错的
        }
        
        A a = new B();  // 向上造型的对象a的声明类为A类,可以调用A类中的fun(),这个方法在任意位置都能被访问
        a.fun();  // 向上造型调用方法,方法的具体执行看子类(B类),执行B类的fun(),这个方法只能在同包范围内访问
        // 此时前后矛盾,证明“一大”原则正确
        
      2. 如果父类的方法返回值类型是引用类型,那么子类的方法返回值类型要么和父类的方法返回值类型一致,要么是父类方法返回值类型的子类

        class A{}
        class B extends A{}  // B为A的子类
        class C{
            public B fun(){
                return null;
            }
        }
        class D extends C{
            public A fun(){  // 反证,证明这是错的
                return null;
            }
        }
        
        C c = new D();  // 向上造型的对象c,声明类C类,可以调用C类中的fun(),返回的是B类型的对象,就能调用B类的方法
        A a = c.fun();  // 向上造型对象调用方法,方法的具体执行看子类(D类),调用D类中的fun(),返回的是A类的对象a,a就可以调用到A类的方法
        // 此时A类中不会包含B类中所有方法,矛盾,证明“一小”的原则是正确的
        

static

  • 关键字,代表静态

  • 可以修饰变量、方法、代码块、内部类

  • static 修饰变量

  • 静态变量 static 变量的定义

  • 静态变量属于类,所以静态变量也称为类变量

  • 生命周期:静态变量随着类的加载(静态常量池)而被加载到方法区的静态区中,在静态区时会对静态变量赋予系统默认初始值,类加载之后不再移除,直到整个程序运行结束,静态变量直到类被移除才会释放

  • 调用静态变量时可以通过类名.静态变量名的形式来调用也可以通过对象.静态变量名调用,为了提高程序可读性,建议通过类名.静态变量名的形式调用。

  • 应用场景:类创建的所有的对象都会对静态变量进行共享 (如果有共享的场景就可以使用静态变量)

  • System.in, System.out,in和out都是静态变量

  • 注意:

    1. 构造代码块当中不能定义静态变量
      • 类加载之后才能创建对象,构造代码块在创建对象时执行,静态变量在类加载时就要初始化
    2. 构造方法不能定义静态变量,原因同上
    3. 构造方法构造代码块中可以给静态变量赋值
    public class StaticDemo {
        public static void main(String[] args) {
            Person p = new Person();
            p.name = "Potter";
            p.age =18;
            p.gender = '男';
            p.eat();
            System.out.println("name:"+p.name+",gender"+p.gender);
    
            Person p1 = new Person();
            p1.name = "lili";
            p1.gender = '女';
            p.eat();
            System.out.println("name:"+p1.name+",gender:"+p1.gender);
            System.out.println("name:"+p.name+",gender:"+p1.gender);
    
         }
    }
    
    class Person{
        String name;
        int age;
        static char gender;
    
        public void eat(){
            System.out.println("People need to eat");
        }
    }
    

    内存图:

在这里插入图片描述

  • static修饰方法

  • static修饰方法

  • 生命周期

    • 静态方法随着类的加载(静态常量池)而加载到方法区的静态区,不会赋予初始值。当调用静态方法时,静态方法会被加载到栈中执行。
  • 调用格式: a.类名.静态方法名()(推荐) b.对象.静态方法名()

  • 注意:

    静态信息只能直接调用静态信息不能直接调用非静态信息,非静态信息可以直接调用非静态信息和静态信息

    1. 静态方法不能定义静态变量
      • 静态方法随着类加载到方法区的静态区中(只是存储方法,并未执行);被调用时才开始运行程序
      • 静态变量在类加载时加载到静态区并初始化
    2. 静态方法不能直接调用非静态方法
      • 非静态变量和非静态方法必须通过对象来调用
      • 当使用类名来调用静态方法时,可能没有创建对象
      • 可以在调用非静态方法之前创建对象,通过对象来调用非静态方法
    3. 静态方法(如 main方法)中不能使用this/super关键字
      this 当前对象 super 父类对象
    4. 子类可以继承父类的静态方法
    5. 静态方法可以重载,但是不能重写
      • 静态方法是和类绑定的
      • 重写针对的是对象 — 与对象一个级别
    6. 子类可以存在与父类方法签名一致的方法,父子类中方法签名一致的方法要么全是静态,要么全是非静态,否则会报错
  1. 静态代码块

    • static修饰构造代码块
    • 在类被使用时执行,包括创建类的对象,调用类的静态方法以及调用类的静态属性
    • 所有的静态信息(静态代码块,静态方法,静态变量)都只加载一次,预先加载一些资源,给静态变量初始化
    • 父子类之间执行顺序(父类静态信息(顺序)-子类静态信息(顺序)-父类对象级别(属性、构造代码块、构造方法)-子类对象级别(属性、构造代码块、构造方法))
    • 注意:
      1. 静态代码块中不可以定义静态变量
      2. 静态代码块可以给静态变量赋值
    class A{
        static int i = 0;
        static{  // 静态代码块 --- 不能出现非静态属性 --- 与类同级 --- 预先加载一些资源,静态变量初始化
            i = 20System.out.println("静态代码块");
        }
        public static void method(){
            System.out.println("静态方法");
        }
    }
    
    public class StaticDemo2 {
        public static void main(String[] args) {
            A a = new A();  // 静态代码块
            System.out.println(A.i);  // 0
            System.out.println("------------------------");
            A.method();  // 静态方法
        }
    }
    

    示例:

    public class StaticTest3 {
        public static void main(String[] args) {
            System.out.println(STDemo1.x + "," + STDemo1.y);
            // 2, 1
        }
    }
    class STDemo1{
        /*
               加载   执行第一步  执行第二步  执行第三步
           sd  null     0x01      0x01       0x01
           x    0        1         2          2
           y    0        1         1          1
         */
        static STDemo1 sd = new STDemo1(); // 1
        static int x = 2; // 2
        static int y;  // 3
        public STDemo1(){
            x ++;  // 1
            y ++;  // 1
        }
    }
    

final

  • 关键字 — 修饰符
  • 可以修饰数据、方法和类
  1. final 修饰数据

    • final 变量的定义

    • final 修饰的数据就是最终值(无法改变的值)

    • final修饰基本类型数据,基本类型数据的值无法改变

    • final修饰的引用数据类型,引用数据类型的地址值无法改变,元素值依然可以改变

    • 如果final修饰的是成员变量且没有进行初始化,需要保证在创建对象之前要进行初始化

    • 如果final修饰的是静态常量且没有进行初始化,保证在类加载完成之前进行初始化

      class FDemo{
          final int i1 = 2;
          // 如果成员变量没有进行初始化,要保证对象创建之前可以给定最终值
          final int i2;
          final int i3;
          // 如果i是静态常量没有进行初始化,要保证在类加载完成之前进行初始化
          final static int i;
      
          // 构造代码块 -- 保证创建对象之前可以给定最终值
          {
              i2 = 2;
          }
          // 构造方法 -- 保证创建对象之前可以给定最终值
          public FDemo(){
              i3 = 7;
          }
      
          static {
              i = 4;
          }
      }
      
  2. final修饰方法

    final修饰的方法可以进行重载但不能进行重写

  3. final修饰类

    final修饰的最终类,没有子类(不能被继承),但是可以有父类

abstract

  • 关键字—修饰符 方法、类

  • 如果所有的子类都对父类中某个方法进行了不同程度的重写,那么父类的这个方法的方法体就没有实际意义,把方法体去掉加上abstract修饰就变成了抽象方法

  • 如果一个类中含有抽象方法那么这个类就必须是抽象类。

  • 如果一个普通类继承抽象类那么需要重写所有的抽象方法,如果不想重写所有的抽象方法就要变成抽象类

  • 注意:

    1. 抽象类可以定义属性和方法
    2. 抽象类不一定含有抽象方法
    3. 抽象类可以定义构造方法,不能创建对象
    4. final/static/private不可以修饰抽象方法 — 抽象方法需要被重写所以不能
    5. final不能修饰抽象类— 抽象类需要被继承抽象方法才能被重写
    6. 抽象方法可以支持重载

    示例:

    public class AbstractDemo {
        public static void getPerimeter(Graph g){
            System.out.println(g.getPerimeter());
        }
        public static void getArea(Graph g){
            System.out.println(g.getArea());
        }
        public static void main(String[] args) {
            getPerimeter(new Rectangle(2.0, 4.0));
            getPerimeter(new Circular(2.0));
            getPerimeter(new Square(2.0));
            getArea(new Rectangle(2.0, 4.0));
            getArea(new Circular(2.0));
            getArea(new Square(2.0));
        }
    }
    
    abstract class Graph{
        // 属性
        private double width;
        private double length;
    
        // 有参构造
        public Graph(double width, double length) {
            this.width = width;
            this.length = length;
        }
    
        public double getWidth() {
            return width;
        }
    
        public double getLength() {
            return length;
        }
    
    
        // 方法,周长
        abstract public double getPerimeter();
    
        // 方法,面积
        abstract public double getArea();
    
    }
    
    class Rectangle extends Graph{
    
        // 手动调用父类的有参构造
        Rectangle(double width, double length){
            super(width, length);
        }
    
        // 重写
        public double getPerimeter(){
            return 2 * (getWidth() + getLength());
        }
        public double getArea(){
            return getWidth() * getLength();
        }
    
    }
    
    class Square extends Rectangle{
        // 调用父类的有参构造
        Square(double width){
            super(width, width);
        }
    }
    
    class Circular extends Graph{
    
        public Circular(double r) {
            super(r,r);
        }
        public double getPerimeter(){
            return 2 * getWidth() * 3.14;
        }
        public double getArea(){
            return 3.14 * getWidth() * getWidth();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值