Java-SE 面向对象 —— 类与对象,成员变量与方法,参数传递,三大特征-封装继承与多态,抽象类与接口,内部类

27 篇文章 0 订阅
7 篇文章 0 订阅

Java-SE 面向对象

文章目录

1 类和对象

1.1 类和对象的理解

类的组成:

  • 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
  • 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)

类和对象的关系:

  • 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
  • 对象:是能够看得到摸的着的真实存在的实体
  • 简单理解:类是对事物的一种描述,对象则为具体存在的事物

1.2 类和对象在内存上的理解

成员变量: 存储在对象的堆内存中。每个对象都有自己的堆内存空间,其中包含其成员变量的值。

方法调用: 方法的调用和执行发生在栈内存上。

1.2.1 成员变量使用:

image-20231125162431078

代码运行结果:

image-20231125162333093
1.2.2 成员方法调用:
image-20231125162610831
1.2.3 多个对象的内存图
1.2.3.1 多个不同的对象:
  • 成员变量使用过程
3
  • 成员方法调用过程
4
  • 总结:

    多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份

1.2.3.2 多个对象指向相同内存:
4
  • 总结

    当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)

    只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。

1.3 成员变量和局部变量

public class VariableExample {
    // 成员变量
    private int memberVariable;

    // 成员方法
    public void exampleMethod() {
        // 局部变量
        int localVar1 = 10;
        int localVar2 = 20;

        System.out.println("Local Variable 1: " + localVar1);
        System.out.println("Local Variable 2: " + localVar2);

        // 使用成员变量
        memberVariable = localVar1 + localVar2;
        System.out.println("Member Variable: " + memberVariable);
    }

    public static void main(String[] args) {
        // 创建对象
        VariableExample exampleObject = new VariableExample();

        // 调用对象的方法
        exampleObject.exampleMethod();
    }
}
成员变量和局部变量的区别
  1. 类中位置不同:成员变量(类中方法外)局部变量(方法内部)
  2. 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
  3. 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
  4. 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)

1.4 方法

方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集

本文仅对Java中方法做大致介绍

1.4.1 无参方法
public static void method (    ) {
	// 方法体;
}
1.4.2 带参方法
public static void 方法名 (参数1) {
	方法体;
}

public static void 方法名 (参数1, 参数2, 参数3...) {
	方法体;
}

可变参数:

在Java中,如果希望一个方法接受不确定数量的参数,可以使用可变参数(Varargs)的语法。

可变参数允许你指定一个参数,其数量可以是可变的,而不需要提前声明参数的数量。在方法定义中使用省略号 … 表示可变参数。

public static void printNumbers(int... numbers) {
 for (int number : numbers) {
     System.out.print(number + " ");
 }
 System.out.println();
}

public static void main(String[] args) {
 // 调用方法时传入不确定数量的参数
 printNumbers(1, 2, 3);
 printNumbers(4, 5, 6, 7, 8);
 printNumbers(9);
}

可变参数使用的注意事项:

可变参数在方法参数列表中必须是最后一个参数,而且每个方法最多只能有一个可变参数。此外,可变参数实际上被视为数组,因此方法内部可以像处理数组一样处理可变参数。

public class VariableArgumentsExample {
 public static void exampleMethod(String firstArg, int... numbers) {
     // 方法体
 }
}
1.4.3 带返回值方法
public static 数据类型 方法名 ( 参数 ) { 
	return 数据 ;
}
1.4.4 方法的注意事项
  • 方法不能嵌套定义
  • void表示无返回值,可以省略return,也可以单独的书写return,后面不加数据
1.4.5 构造方法
  • 功能:主要是完成对象数据的初始化

  • 格式:

    public class 类名{

    ​ 修饰符 类名( 参数 ) {

    ​ }

    }

  • 注意:

    • 如果没有定义构造方法,系统将给出一个默认的无参数构造方法

    • 如果定义了构造方法,系统将不再提供默认的构造方法

    • 如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法

  • 建议:

    • 无论是否使用,都手工书写无参数构造方法

    • 可以使用带参构造,为成员变量进行初始化

  • setXxx和getXxx访问控制 - 封装思想

    setget 方法通常不被归类为构造方法。它们是一种常见的编程约定,用于提供对对象的私有属性的访问和修改。

1.4.6 方法重载
1.4.6.1 方法重载概念

方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载:

  1. 多个方法在同一个类中
  2. 多个方法具有相同的方法名
  3. 多个方法的参数不相同,类型不同或者数量不同
1.4.6.2 方法重载注意事项:
  1. 重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
  2. 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载

正确方法重载:

public class MethodDemo {
	public static void fn(int a) {
    	//方法体
    }
    public static int fn(double a) {
    	//方法体
    }
}

public class MethodDemo {
	public static float fn(int a) {
    	//方法体
    }
    public static int fn(int a , int b) {
    	//方法体
    }
}

错误的方法重载 :

public class MethodDemo {
	public static void fn(int a) {
    	//方法体
    }
    public static int fn(int a) { 	/*错误原因:重载与返回值无关*/
    	//方法体
    }
}

public class MethodDemo01 {
    public static void fn(int a) {
        //方法体
    }
} 
public class MethodDemo02 {
    public static int fn(double a) { /*错误原因:这是两个类的两个fn方法*/
        //方法体
    }
}

方法重载避免了过度的方法命名,提高了代码的可读性和简洁性,使得方法的参数的类型及个数更加的灵活

1.4.7 方法的参数传递

核心:

  • 基本数据类型的参数,形式参数的改变,不影响实际参数
  • 对于引用类型的参数,形式参数的改变,影响实际参数的值
1.4.7.1 基本数据类型参数

基本数据类型的参数,形式参数的改变,不影响实际参数

依据:每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失

image-20231125130818449

1.4.7.2 应用数据类型参数

引用类型的参数,形式参数的改变,影响实际参数的值

依据:引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果

image-20231125130951504

2 面向对象三大特征 - 封装,继承,多态

2.1 封装

  • 定义: 封装是将数据和方法(行为)打包到一个单一的单元(类)中,同时对外部隐藏数据的实现细节。
  • 实现: 使用私有化成员变量,通过公共的方法(getter和setter)来访问和修改数据。这样可以防止直接访问对象的内部状态,强调了对象内部和外部的隔离。

2.2 继承

2.2.1 继承的概念

继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法

2.2.2 实现继承的格式
  • 继承通过extends实现

  • 格式:class 子类 extends 父类 { }

    public class Fu {
        public void show() {
            System.out.println("show方法被调用");
        }
    }
    public class Zi extends Fu {
        public void method() {
            System.out.println("method方法被调用");
        }
    }
    public class Demo {
        public static void main(String[] args) {
            //创建对象,调用方法
            Fu f = new Fu();
            f.show();
    
            Zi z = new Zi();
            z.method();
            z.show();
        }
    }
    
2.2.3 继承的好处
  • 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
  • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
  • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
2.2.4 继承的弊端
  • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
2.2.5 继承的应用场景

使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承

is…a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类

2.2.6 继承中成员的访问
2.2.6.1 变量的访问

在子类方法中访问一个变量,采用的是就近原则

  1. 子类局部范围找

  2. 子类成员范围找

  3. 父类成员范围找

  4. 如果都没有就报错(不考虑父亲的父亲)

Super
  • this&super关键字:

    • this:代表本类对象的引用
    • super:代表父类存储空间的标识(可以理解为父类对象引用)
  • this和super的使用分别

    • 成员变量:
      • this.成员变量 - 访问本类成员变量
      • super.成员变量 - 访问父类成员变量
    • 成员方法:
      • this.成员方法 - 访问本类成员方法
      • super.成员方法 - 访问父类成员方法
  • 构造方法:

    • this(…) - 访问本类构造方法
    • super(…) - 访问父类构造方法
  • super的内存图

    **核心:**对象在堆内存中,会单独存在一块super区域,用来存放父类的数据

    image-20231125211626856

2.2.6.2 构造方法的访问

注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

​ 子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

提问:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 在父类中自己提供一个无参构造方法

最好的办法仍是自己给出无参构造方法

2.2.6.3 成员方法的访问

当我们通过子类对象访问一个方法时

  1. 子类成员范围找
  2. 父类成员范围找
  3. 如果都没有就报错(不考虑父亲的父亲…)
2.2.6.4 方法重写

1、概念

  • 方法重写是指在子类中重新定义(覆盖)父类中已经存在的方法

2、应用场景

  • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容

    // 父类
    class Animal {
        // 方法被子类重写
        public void makeSound() {
            System.out.println("Animal makes a sound");
        }
    }
    
    // 子类
    class Dog extends Animal {
        // 重写父类的方法
        @Override
        public void makeSound() {
            System.out.println("Dog barks");
        }
    }
    
    // 主方法用于测试
    public class InheritanceExample {
        public static void main(String[] args) {
            // 创建子类对象
            Dog myDog = new Dog();
    
            // 调用被重写的方法
            myDog.makeSound();
        }
    }
    

3、Override注解

  • 用来检测当前的方法,是否是重写的方法,起到【校验】的作用

2.3 多态

2.3.1 多态的概述
  • 什么是多态

    ​ 同一个对象,在不同时刻表现出来的不同形态

  • 多态的前提 重点

    • 要有继承或实现关系
    • 要有方法的重写(重写父类中的方法,不一定要全部重写,但补不重写,无意义)
    • 要有父类引用指向子类对象
2.3.2 多态中的成员访问特点 重点
  • 成员访问特点

    • 成员变量

      编译看父类,运行看父类:编译和运行时期都是参考引用变量所属类中的成员,且在编译期间,如果没有,则编译失败,所以也不能使用子类的特有成员

    • 成员方法

      编译看父类,运行看子类:当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法

  • 代码演示

    • 动物类

      public class Animal {
          public int age = 40;
      
          public void eat() {
              System.out.println("动物吃东西");
          }
      }
      
    • 猫类

      public class Cat extends Animal {
          public int age = 20;
          public int weight = 10;
      
          @Override
          public void eat() {
              System.out.println("猫吃鱼");
          }
      
          public void playGame() {
              System.out.println("猫捉迷藏");
          }
      }
      
    • 测试类

      public class AnimalDemo {
          public static void main(String[] args) {
              //有父类引用指向子类对象
              Animal a = new Cat();
      
              System.out.println(a.age); //输出40
      //        System.out.println(a.weight);//编译失败
      
              a.eat();//输出猫吃鱼
      //        a.playGame();//编译失败
          }
      }
      
2.3.3 多态的好处和弊端重点
  • 好处

    ​ 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

  • 弊端

    ​ 不能使用子类的特有成员

2.3.4 多态中的转型 重点
  • 向上转型 (多态中的默认过程)

    ​ 父类引用指向子类对象就是向上转型

  • 向下转型 (强制过程)

    ​ 格式:子类型 对象名 = (子类型)父类引用;

  • 代码演示

    • 动物类
    public class Animal {
        public void eat() {
            System.out.println("动物吃东西");
        }
    }
    
    • 猫类
    public class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    
        public void playGame() {
            System.out.println("猫捉迷藏");
        }
    }
    
    • 测试类
    public class AnimalDemo {
        public static void main(String[] args) {
            //多态
            //向上转型
            Animal a = new Cat();
            a.eat();
    //      a.playGame();
    
    
            //向下转型
            Cat c = (Cat)a;
            c.eat();
            c.playGame();
        }
    }
    

注意:必须保证对象在创建的时候就是猫,向下转型才可以成为猫;否则,如果最开始为狗,如果要将其强制转换为猫,就会报错。

3 抽象类与接口

3.1 抽象类

当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的实现,这个时候就需要抽象类

在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类

3.1.1 抽象类特点
  • 抽象类和抽象方法必须使用 abstract 关键字修饰

    //抽象类的定义
    public abstract class 类名 {}
    
    //抽象方法的定义
    public abstract void eat();
    
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

  • 抽象类不能实例化,参照多态的方式,通过子类对象实例化,这叫抽象类多态

  • 抽象类的子类

    要么重写抽象类中的所有抽象方法;否则自身也是是抽象类

3.1.2 抽象类成员特点

image-20231126113209490

3.2 接口

Java中的接口更多的体现在对行为的抽象

3.2.1 接口的特点
  • 接口用关键字interface修饰

    public interface 接口名 {} 
    
  • 类实现接口用implements表示

    public class 类名 implements 接口名 {}
    
  • 接口不能实例化

    ​ 参照多态的方式,通过实现类对象实例化,这叫接口多态。

    多态的形式:具体类多态,抽象类多态,接口多态。

  • 接口的子类

    ​ 要么重写接口中的所有抽象方法

    ​ 要么子类也是抽象类

3.2.2 接口的成员特点

image-20231126114107374

接口中的常量是不能修改的

3.2.3 类和接口的关系 重点
  • 类与类的关系:

    继承关系,只能单继承,但是可以多层继承

  • 类与接口的关系

    ​ 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口与接口的关系

    ​ 继承关系,可以单继承,也可以多继承

    // 定义第一个接口
    interface InterfaceA {
        void methodA();
    }
    
    // 定义第二个接口
    interface InterfaceB {
        void methodB();
    }
    
    // 定义继承自两个接口的新接口
    interface CombinedInterface extends InterfaceA, InterfaceB {
        void combinedMethod();
    }
    
3.2.4 抽象类和接口的区别 重点
  • 成员区别

    • 抽象类

      ​ 变量,常量;有构造方法;有抽象方法,也有非抽象方法

    • 接口

      ​ 常量;抽象方法

  • 关系区别

    • 类与类

      ​ 继承,单继承

    • 类与接口

      ​ 实现,可以单实现,也可以多实现

    • 接口与接口

      ​ 继承,单继承,多继承

  • 设计理念区别

    • 抽象类

      ​ 对类抽象,包括属性、行为

    • 接口

      ​ 对行为抽象,主要是行为

4 参数传递

4.1 类名作为形参和返回值

  • 类名作为方法的形参
    • 方法的形参是类名,其实需要的是该类的对象
    • 实际传递的是该对象的【地址值】
  • 类名作为方法的返回值
    • 方法的返回值是类名,其实返回的是该类的对象
    • 实际传递的,也是该对象的【地址值】

4.2 抽象类作为形参和返回值

  • 抽象类作为形参和返回值
    • 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
    • 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象

4.3 接口名作为形参和返回值

  • 接口作为形参和返回值
    • 方法的形参是接口名,其实需要的是该接口的实现类对象
    • 方法的返回值是接口名,其实返回的是该接口的实现类对象

5 内部类

5.1 内部类的基本使用

  • 内部类概念

    在一个类A的内部定义一个类B,类B就被称为内部类

    image-20231126120140140
  • 内部类定义格式

    /*
    	格式:
        class 外部类名{
        	修饰符 class 内部类名{
        	
        	}
        }
    */
    
    class Outer {
        public class Inner {
            
        }
    }
    
  • 内部类的访问特点 重点

    • 内部类可以直接访问外部类的成员,包括私有
    • 外部类要访问内部类的成员,必须创建对象
  • 示例代码:

    /*
        内部类访问特点:
            内部类可以直接访问外部类的成员,包括私有
            外部类要访问内部类的成员,必须创建对象
     */
    public class Outer {
        private int num = 10;
        public class Inner {
            public void show() {
                System.out.println(num);
            }
        }
        public void method() {
            Inner i = new Inner();
            i.show();
        }
    }
    

5.2 成员内部类

  • 成员内部类的定义位置

    • 在类中方法,跟成员变量是一个位置
  • 外界创建成员内部类格式

    • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
    • 举例:Outer.Inner oi = new Outer().new Inner();
  • 成员内部类的推荐使用方案

    • 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
  • 示例代码:

    class Outer {
        private int num = 10;
        private class Inner {
            public void show() {
                System.out.println(num);
            }
        }
        public void method() {
            Inner i = new Inner();
            i.show();
        }
    }
    
    public class InnerDemo {
        public static void main(String[] args) {
    		//Outer.Inner oi = new Outer().new Inner();
    		//oi.show();
            Outer o = new Outer();
            o.method();
        }
    }
    

5.3 局部内部类

  • 局部内部类定义位置

    • 局部内部类是在方法中定义的类
  • 局部内部类方式

    • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
    • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
  • 示例代码

    class Outer {
        private int num = 10;
        public void method() {
            int num2 = 20;
            class Inner {
                public void show() {
                    System.out.println(num);
                    System.out.println(num2);
                }
            }
            Inner i = new Inner();
            i.show();
        }
    }
    public class OuterDemo {
        public static void main(String[] args) {
            Outer o = new Outer();
            o.method();
        }
    }
    
    

5.4 匿名内部类

  • 匿名内部类的前提

    • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
  • 匿名内部类的格式

    • 格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }

    • 举例:

      new Inter(){
          @Override
          public void method(){}
      } 
      
  • 匿名内部类的细节

    • 匿名内部类可以通过多态的形式接受

      Inter i = new Inter(){
        @Override
          public void method(){
              
          }
      }
      
  • 匿名内部类直接调用方法

    interface Inter{
        void method();
    }
    
    class Test{
        public static void main(String[] args){
            new Inter(){
                @Override
                public void method(){
                    System.out.println("我是匿名内部类");
                }
            }.method();	// 直接调用方法
        }
    }
    

5.5 匿名内部类在开发中的使用

  • 匿名内部类在开发中的使用

    • 通常用于简化代码

      当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码

  • 示例代码:

    interface Jumpping {
        void jump();
    }
    class Cat implements Jumpping {
        @Override
        public void jump() {
            System.out.println("猫可以跳高了");
        }
    }
    class Dog implements Jumpping {
        @Override
        public void jump() {
            System.out.println("狗可以跳高了");
        }
    }
    class JumppingOperator {
        public void method(Jumpping j) { //new Cat();   new Dog();
            j.jump();
        }
    }
    class JumppingDemo {
        public static void main(String[] args) {
            //需求:创建接口操作类的对象,调用method方法
            JumppingOperator jo = new JumppingOperator();
            Jumpping j = new Cat();
            jo.method(j);
    
            Jumpping j2 = new Dog();
            jo.method(j2);
            System.out.println("--------");
    
            // 匿名内部类的简化
            jo.method(new Jumpping() {
                @Override
                public void jump() {
                    System.out.println("猫可以跳高了");
                }
            });
    		// 匿名内部类的简化
            jo.method(new Jumpping() {
                @Override
                public void jump() {
                    System.out.println("狗可以跳高了");
                }
            });
        }
    }
    
  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值