方法

方法

一、概述

方法,也称函数,如果想要重复一段或者多段代码块的使用,可以将这些代码封装成一个方法,方法具体表现为某种行为,使用方法可以提高代码的复用性。

二、方法的种类

1.构造方法

构造方法的概念

构造方法是一种特殊的方法,它的名字必须与类名完全相同,并且没有返回类型(即使是void也不行)。构造方法的主要用途是在创建类的新实例时对这个实例进行初始化。构造方法在使用new关键字创建对象时被自动调用。

构造方法的使用示例

以下是构造方法使用的一个示例:

public class Person {
    private String name;
    private int age;

    // 无参构造方法
    public Person() {
        this.name = "Unknown";
        this.age = 0;
        System.out.println("Person created with no name and age.");
    }

    // 带一个参数的构造方法
    public Person(String name) {
        this.name = name;
        this.age = 0;
        System.out.println("Person created with name: " + name);
    }

    // 带两个参数的构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person created with name: " + name + " and age: " + age);
    }

    // 成员变量的getter和setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

// 测试类
public class TestPerson {
    public static void main(String[] args) {
        // 使用无参数构造方法创建对象
        Person person1 = new Person();

        // 使用带一个参数的构造方法创建对象
        Person person2 = new Person("Alice");

        // 使用带两个参数的构造方法创建对象
        Person person3 = new Person("Bob", 30);
    }
}

在这个例子中,Person类定义了三个构造方法:一个无参构造方法,一个带有一个字符串参数的构造方法,以及一个带有两个参数(字符串和整数)的构造方法。在TestPerson类的main方法中,通过这三个构造方法创建了三个Person对象。

注意事项

  • 不可被static, abstract, final, synchronized修饰:构造方法不能被static、abstract、final或synchronized等关键字修饰。这是因为构造方法是用于初始化对象的,而不是静态成员或方法的一部分,也不是线程安全的保障手段。
  • 构造方法的重载:构造方法的重载可以使得在创建对象时提供不同数量或类型的参数,从而初始化对象的状态。

通过构造方法,我们能够确保对象在创建时就已经处于一个合理的状态,这对于后续的逻辑处理非常重要。

2.静态方法

  1. 关键字static修饰的方法称为静态方法,也叫类方法,静态方法不属于对象,而是属于类,随着类的加载而加载,优先于构造方法执行。
  2. 静态方法可以直接使用类名.方法直接调用,如果静态方法位于本类中可以直接省略类名直接调用
  3. 在静态方法中,可以访问静态方法,可以引用类变量(被static修饰的变量),不能访问非静态方法与变量,不能使用super和this关键字

3.非静态方法

  1. 是不含有static关键字修饰的普通方法,又称为实例方法,成员方法。属于对象的,不属于类的。(成员属性,成员方法是属于对象的,必须通过new关键字创建对象后,再通过对象调用)。

  2. 在非静态方法中,即可以调用非静态方法,也可以调用静态方法,可以引用类变量和成员变量,可以使用关键字super和this

public class Text3 {
	
	int age = 18;//成员变量
	static String name = "迪丽热巴";//类变量
	
	public static void main(String[] args) {
		Text3 t1 =  new Text3();
		method();//本类中调用静态方法,直接省略类名
		t1.method1();//调用成员方法
	}
	
	//静态方法
	static void method() {
		System.out.println("静态方法");
		//访问属性
		System.out.println(name);
		//System.out.println(age);//报错,静态不能访问非静态
		//t1.method();//报错
	}
	
	//非静态方法,也叫成员方法
	void method1() {
		System.out.println("非静态方法1");
		method();//既可以调用静态方法
		new Text3().method2();//又可以调用非静态方法
		
		System.out.println(name);//既可以访问静态属性
		System.out.println(age);//又可以访问非静态属性
	}
	void method2() {
		System.out.println("非静态方法2");
	}
}

这段代码定义了一个名为 Text3 的类,并在其中展示了静态方法和非静态方法的使用。以下是代码的详细解释:

  1. 成员变量和类变量

    • int age = 18; 是一个非静态成员变量(实例变量),每个 Text3 类的实例都有自己的 age 值。
    • static String name = "迪丽热巴"; 是一个静态类变量(类变量),所有 Text3 类的实例共享这个 name 变量。
  2. main 方法

    • 创建了 Text3 类的一个实例 t1
    • 调用静态方法 method(),不需要对象实例,可以直接通过类名调用。
    • 调用非静态方法 method1(),需要通过对象实例 t1 调用。
  3. 静态方法 method

    • 静态方法可以被类名直接调用,不需要创建类的实例。
    • 在静态方法中,可以访问静态变量,如 name,但不能直接访问非静态变量,如 age
    • 静态方法不能直接访问非静态方法,因为非静态方法依赖于类的实例。
  4. 非静态方法 method1

    • 非静态方法需要通过对象实例调用。
    • 在非静态方法中,可以访问静态变量和非静态变量,也可以调用静态方法和非静态方法。
    • method1 方法中调用了静态方法 method 和另一个非静态方法 method2
  5. 非静态方法 method2

    • 这个方法简单地打印出 "非静态方法2"。

输出
当运行这段代码时,输出将如下所示:

静态方法
迪丽热巴
非静态方法1
非静态方法2
迪丽热巴
18

解释

  • 首先,调用 method(),打印 "静态方法" 和类变量 name 的值。
  • 然后,调用 method1(),打印 "非静态方法1",接着创建一个新的 Text3 对象并调用 method2(),打印 "非静态方法2"。
  • method1() 中,打印静态变量 name 和非静态变量 age 的值。

注意

  • 静态方法属于类本身,而非静态方法属于类的实例。
  • 静态方法中不能直接访问非静态变量或调用非静态方法,因为它们需要一个对象实例。
  • 非静态方法可以访问类的任何变量和调用任何方法,无论是静态的还是非静态的。

4.抽象方法

关键字abstract修饰的方法称为抽象方法,抽象方法必须定义在抽象类(abstract修饰的类称为抽象类)中,抽象类中既有抽象方法,也有非抽象方法,但是抽象方法必须定义在抽象类中,并且抽象方法没有方法体

public class Text3 {
	public static void main(String[] args) {
		Cat cat = new Cat();
		cat.cry();
	    }
	}

	// 定义一个抽象类
	abstract class Animal {
	    String name;
	    int age;
	    // 抽象方法
	    public abstract void cry(); // 不确定动物怎么叫的。定义成抽象方法,来解决父类方法的不确定性。
	    							//抽象方法在父类中不能实现,所以没有方法体。但在后续在继承时,要具体实现此方法。
	}
	// 抽象类可以被继承
	// 当继承的父类是抽象类时,需要将抽象类中的所有抽象方法全部实现。
	class Cat extends Animal {
	    // 实现父类的cry抽象方法
	    public void cry() {
	        System.out.println("猫叫:苗苗苗");
	    }
	}

注:静态方法和非静态方法的区别(生命周期不同)

静态方法的生命周期跟相应的类一样长,静态方法和静态变量会随着类的定义而被分配和装载入内存中。一直到线程结束,静态属性和方法才会被销毁。(也就是静态方法属于类)
非静态方法的生命周期和类的实例化对象一样长,只有当类实例化了一个对象,非静态方法才会被创建,而当这个对象被销毁时,非静态方法也马上被销毁。(也就是非静态方法属于对象)

三、下面是构造方法与普通方法的一些关键区别:

构造方法的特点:

  • 名称与类名相同:构造方法的名称必须与定义它的类名完全一致,而普通方法可以自由命名。
  • 没有返回类型:构造方法不返回任何值,因此不需要声明返回类型。而普通方法可以有不同的返回类型,包括void
  • 调用方式:构造方法是在创建对象时自动调用的,通过new关键字来创建对象,而普通方法需要通过已创建的对象或类名(如果是静态方法)显式调用。
  • 初始化职责:构造方法主要用于初始化对象的状态,如分配内存、设置默认值等。普通方法则用于实现类的具体功能,执行特定的任务。
  • 重载:构造方法可以被重载,即一个类可以定义多个构造方法,通过不同的参数列表来实现构造方法的重载。
  • 修饰符:构造方法不能被staticfinalsynchronizedabstractnative修饰,而普通方法可以使用这些修饰符。
  • 默认构造方法:如果一个类没有定义任何构造方法,Java编译器会自动为这个类提供一个默认的无参构造方法。一旦定义了构造方法,即使是一个,编译器也不会再提供默认的无参构造方法。
  • 可以有参数:构造方法可以带有参数,这些参数可以用于初始化对象的状态。
  • 可以重载:一个类可以有多个构造方法,只要它们的参数列表不同。这种现象称为构造方法的重载。

普通方法的特点:

  • 名称:普通方法的名称可以自由定义,只要符合编程语言的规则。
  • 返回类型:普通方法可以有各种返回类型,包括void(表示没有返回值)。
  • 主要用途:普通方法用于执行特定的功能或计算,可以修改对象状态或者返回计算结果。
  • 调用方式:普通方法需要通过已创建的对象或类名(如果是静态方法)显式调用。

示例

假设有一个名为Person的类,下面是一个构造方法和普通方法的例子:

public class Person {
    private String name;
    private int age;

    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 普通方法
    public void introduce() {
        System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
    }
}

在这个例子中,Person类有两个属性:nameage。构造方法接受这两个参数,并将它们赋值给相应的成员变量。introduce方法是一个普通方法,它输出一个字符串介绍这个人的信息。

结论

构造方法与普通方法的主要区别在于它们的用途和调用方式。构造方法专注于对象的初始化,并在对象创建时自动调用,而普通方法则用于执行业务逻辑,并需要显式调用。理解这些区别有助于更有效地编写和维护面向对象的程序。

四、构造方法和普通方法分别可以调用哪种方法

构造方法和普通方法在Java中都可以调用其他的方法,但它们之间的调用方式有一些限制和规范。

构造方法可以调用的方法:

  1. 其他构造方法:构造方法内部可以调用同一个类中的其他构造方法,这种调用必须发生在构造方法的第一行,并且使用this关键字加上参数列表来指定要调用的构造方法。这种调用方式通常用于代码复用。

    public class Example {
        public Example() {
            // 调用带有参数的构造方法
            this("default");
        }
    
        public Example(String value) {
            // 初始化逻辑
        }
    }
    
  2. 普通方法:构造方法也可以调用类中的任何普通方法,但需要注意的是,由于构造方法在对象创建时调用,因此在调用普通方法时,对象的状态可能尚未完全初始化。

    public class Example {
        private int value;
    
        public Example() {
            setValue(10);
            printValue();
        }
    
        private void setValue(int newValue) {
            value = newValue;
        }
    
        public void printValue() {
            System.out.println(value);
        }
    }
    

普通方法可以调用的方法:

  1. 其他普通方法:普通方法可以调用类内的其他普通方法,这种方式非常灵活,可以根据需要在任何地方调用。

    public class Example {
        public void methodA() {
            methodB();
        }
    
        private void methodB() {
            // 实现细节
        }
    }
    
  2. 构造方法:普通方法不能直接调用构造方法,因为构造方法是在对象创建时由JVM自动调用的,而普通方法是在对象创建之后才调用的。

    // 错误示范:
    public class Example {
        public void someMethod() {
            new Example(); // 这实际上是在创建一个新的对象
        }
    }
    

特殊情况:

  • 静态方法:不论是构造方法还是普通方法,都可以调用类中的静态方法,因为静态方法并不依赖于任何特定对象的存在。

    public class Example {
        public static void staticMethod() {
            // 实现细节
        }
    
        public Example() {
            staticMethod();
        }
    
        public void instanceMethod() {
            staticMethod();
        }
    }
    

总之,构造方法主要用于初始化对象,而普通方法则用于实现对象的行为。构造方法可以调用其他构造方法或普通方法来辅助初始化过程,而普通方法则主要用于实现业务逻辑,并且可以调用类中的其他普通方法或静态方法。需要注意的是,构造方法不应直接被普通方法调用,因为构造方法是在对象创建时由系统自动调用的。

五、方法的声明

1.组成

访问权限符 返回值  方法名(参数列表){
	方法体
}
public viod method(){
	System.out.println(“Hello World!!!”);
}

2.访问权限符

表示该方法被访问的权限,主要有以下几种

public

(1)定义:public是公共的,被public所修饰的成员可以在任何类中都能被访问到。
(2)作用域:public能来修饰类,在一个java源文件中只能有一个类被声明为public,而且一旦有一个类为public,那这个java源文件的文件名就必须要和这个被public所修饰的类的类名相同,否则编译不能通过。public用来修饰类中成员(变量和方法),被public所修饰的成员可以在任何类中都能被访问到。通过操作该类的对象能随意访问public成员。public在类的继承上的体现,被public所修饰的成员能被所有的子类继承下来。

protected

(1)定义:protected是受保护的,受到该类所在的包所保护。
(2)作用域:被protected所修饰的成员会被位于同一package中的所有类访问到。同时,被protected所修饰的成员也能被该类的所有子类继承下来。(注意:这里是指同一个package或者不同的package中的子类都能访问)

default

(1)定义:default是默认,缺省的,即在成员的前面不写任何的访问修饰符的时候,默认就是友好的。所谓友好的,是对同一package的类友好。
(2)作用域:同一package中的所有类都能访问。被friendly所修饰的成员只能被该类所在同一个package中的子类所继承下来。(也就是说只有在同一个package中的子类才能访问到父类中friendly修饰的成员)

private

(1)定义:private是私有的,即只能在当前类中被访问到,它的作用域最小。
(2)作用域:private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被private修饰的成员,只能在定义它们的类中使用,在其他类中不能调用。

以下是四种主要的访问修饰符及其对应的访问范围:

访问修饰符 描述 示例代码
public 最宽松的访问级别,允许任何类访问该成员。 public class MyClass { public void method() {} }
protected 成员可以被同一个包内的类访问,也可以被不同包中的子类访问。 protected class MyClass { protected void method() {} }
默认(无修饰符) 成员仅能被同一个包内的类访问,这种权限也称为包级权限。 class MyClass { void method() {} }
private 最严格的访问级别,成员只能被其所在的类访问。 private class MyClass { private void method() {} }
访问修饰符 同一类中 同一包 不同包中的非子类 不同包中的子类 同包的子类
public
protected
默认
private

这里,“同一文件”意味着成员可以被定义在同一源文件中的其他类访问;“同一包”指的是成员可以被位于同一个包中的类访问;“不同包中的非子类”是指那些不在同一包内的类,且不是继承关系;而“不同包中的子类”则是指位于不同包内的类,但是子类继承了拥有成员的那个类。

请注意,这里提到的“同一文件”实际上并不是标准的术语,但它有助于理解在同一个源文件中定义的内部类或嵌套类如何访问外部类的成员。对于private成员来说,即使在同一文件中,也只有包含该成员的类能够访问到它。

3.返回值:

表示方法(函数)要返回的数据类型,int ,double,String等等·,void表示无返回值

4.方法名:

方法的名称,避免使用到java关键字,并且首字母小写,命名时使用驼峰命名法
骆驼式命名法(Camel-Case)又称驼峰式命名法,是电脑程式编写时的一套命名规则(惯例)。正如它的名称CamelCase所表示的那样,是指混合使用大小写字母来构成变量和函数的名字。程序员们为了自己的代码能更容易的在同行之间交流,所以多采取统一的可读性比较好的命名方式。

5.参数列表:

方法声明时可以定义参数列表,调用时需要传入实参

5.1 形参

也叫形式参数,方法声明时候的参数称为形参,如add(int a)

5.2 实参

也叫实际参数,方法调用时传入的参数,如add(1)

public class Text {
public static void main(String[] args) {
	method("字符参数");//括号中为实参
}
private static void method(String str) {//括号中为形参
	System.out.println(str);//将传入的参数打印输出
}
}

5.3 可变参数

1.当参数的个数不定时,可以使用可变参数
2.可变参数的写法:int…a 前面为参数类型,中间加三个点,后面为参数名
3.使用规则:

  • 可变参数只能位于参数列表的最后一个

  • 可变参数有且只有一个

4.可变参数的原理为数组,传入参数时,根据传入的参数自动生成匹配的数组类型,数组长度等于传入的参数个数,数组元素为传入的实参

public class Text2 {
	public static void main(String[] args) {
		int sum = addSum(1,2,3,4,5,6,7,8,9);
		System.out.println(sum);
	}

private static int addSum(int ...a) {
	int add = 0;
	//将数组元素(即传入的参数)求和
	for (int i = 0; i < a.length; i++) {
		add += a[i];
	}
	return add;
}

}

六、方法的声明与调用

public class Text3 {
	//定义4种方法
	private static void method1() {
		System.out.println("无参无返回值的方法");
	}
	private static int method2() {
		System.out.println("无参有返回值的方法");
		return 1;
	}

	private static int method3(String str) {
		System.out.println("有参有返回值的方法");
		return 1;
	}

	private static void method4(String str) {
		System.out.println("有参无返回值的方法");
	}
	
	public static void main(String[] args) {
		//调用方法
		method1();
		method2();
		method3("我");
		method4("你");	
	}
}

对象的创建与构造方法的应用

1.对象是通过构造函数创建的,默认通过调用无参构造方法创建。

2.在Java中,创建对象有两种方式:先创建对象再赋值和边创建边赋值,后者通过有参构造方法实现。

3.有参构造方法可以通过形参接收外部传入的值,并赋给对象的属性。 4.在构造方法中使用this关键字可以引用当前对象的属性或调用其他构造方法。

this关键字的用法

1.this关键字用于指代当前类的实例化对象,可以用于访问对象的属性和方法。

2.在构造方法中,this可以用来调用其他构造方法,但只能在构造方法中使用。

3.this还可以用于区分成员变量和形式参数,避免命名冲突。

构造函数中的错误写法及解决方式

1.在构造函数中直接调用无参构造函数会导致死循环,引发内存溢出错误。

2.正确的做法是根据需要选择调用有参或无参构造函数,避免无限递归。

七、JVM内存结构划分

在JVM(Java Virtual Machine)的内存结构中,堆(Heap)和栈(Stack,通常是指虚拟机栈VM Stack和本地方法栈Native Method Stack的统称,但在这里我们主要讨论虚拟机栈)是两个非常重要的区域,它们在功能、存储内容、生命周期以及管理方式上都有显著的不同。

堆(Heap)

  • 描述:堆是JVM中用于存储对象实例和数组的内存区域。它是JVM管理的最大一块内存区域,也是垃圾收集器管理的主要区域。

  • 特性:

    • 堆是线程共享的,多个线程可以访问同一个堆中的对象。
    • 堆的大小在JVM启动时确定,并可以通过JVM启动参数(如-Xms和-Xmx)进行调整。
    • 堆被划分为新生代(Young Generation)和老年代(Old Generation),新生代又被进一步划分为Eden区、From Survivor区(S0)和To Survivor区(S1)。
    • 堆内存中的对象由垃圾收集器进行回收,当堆内存不足时,会触发垃圾收集。

栈(Stack,主要指虚拟机栈VM Stack)

  • 描述:栈是每个线程私有的内存区域,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当一个方法被调用时,JVM会在这个线程的栈中创建一个栈帧(Stack Frame)来存储这个方法调用的相关信息。

  • 特性:

    • 栈是线程私有的,每个线程都有自己独立的栈空间。
    • 栈的大小在JVM启动时确定,但栈的大小限制通常不是由JVM直接设置的,而是由操作系统和JVM的具体实现决定的。
    • 栈是后进先出(LIFO)的数据结构,当一个方法被调用时,它的栈帧被压入栈顶;当方法执行完成后,它的栈帧被弹出栈顶。
    • 如果栈的深度超过了JVM所允许的最大深度,就会抛出StackOverflowError异常;如果栈在尝试扩展时无法申请到足够的内存空间,就会抛出OutOfMemoryError异常。

堆和栈在JVM内存结构中扮演着不同的角色:

  • 堆用于存储对象实例和数组,是垃圾收集器管理的主要区域,是线程共享的。
  • 栈用于存储局部变量表、操作数栈、动态链接、方法出口等信息,是线程私有的,用于支持方法的调用和执行。

理解堆和栈的区别对于深入理解JVM的内存管理和优化Java程序的性能至关重要。

八、方法的重载

方法重载

方法重载指的是在同一个类中定义多个具有相同名称但参数列表不同的方法。这里的“参数列表”指的是方法参数的个数、类型以及参数的顺序。方法的返回类型和访问修饰符并不能作为判断方法是否重载的标准 。

方法重载的条件

要构成方法重载,必须满足以下条件:

  1. 方法名相同。
  2. 参数列表不同,即参数的个数、类型或顺序至少有一项不同。
  3. 方法的返回类型可以相同也可以不同,但这不是判断重载的关键条件 。

方法重载的目的

方法重载的目的在于提高代码的可读性和可用性,同时也增强了代码的灵活性。通过使用相同的方法名,可以使得代码更加清晰易懂,并且易于维护 。

方法重载的示例

以下是几个方法重载的示例:

public class MathUtil {

    // 重载方法disp,分别接受char和int类型的参数
    public static void disp(char ch) {
        System.out.println("字符:" + ch);
    }

    public static void disp(int num) {
        System.out.println("整数:" + num);
    }

    // 重载方法add,处理不同类型的加法运算
    public static int add(int a, int b) {
        return a + b;
    }

    public static double add(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        MathUtil.disp('A'); // 调用disp(char ch)
        MathUtil.disp(1);   // 调用disp(int num)

        int sumInt = MathUtil.add(2, 3); // 调用add(int a, int b)
        double sumDouble = MathUtil.add(2.5, 3.5); // 调用add(double a, double b)
        
        System.out.println("整数相加的结果是:" + sumInt);
        System.out.println("浮点数相加的结果是:" + sumDouble);
    }
}

在这个示例中,dispadd 方法都进行了重载,通过不同的参数类型或个数实现了不同的功能。当调用这些方法时,Java编译器会根据传递的实际参数来选择合适的方法执行 。

方法重载的注意事项

虽然方法重载提供了便利,但也需要注意以下几点:

  • 方法的返回类型不能作为重载的依据。
  • 仅仅是返回类型不同不足以构成方法的重载。
  • 参数列表的差异决定了方法的重载。

结论

方法重载是Java语言中一种重要的特性,它允许程序员在同一个类中定义多个同名但参数列表不同的方法,从而提高代码的可读性和灵活性。正确使用方法重载可以帮助编写更清晰、更易于维护的代码。

九、方法重写

方法重写的基本概念

  • 定义:当子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表时,我们说子类中的方法覆盖(overriding)了父类中的方法。
  • 目标:子类可以改变继承自父类的方法的行为,以适应子类特有的需求。
  • 好处:提高了代码的灵活性和可扩展性,同时也支持了多态性。

方法重写的规则

  1. 参数列表必须完全与被重写方法的相同;
  2. 返回类型必须完全与被重写方法的返回类型相同;
  3. 访问权限不能比父类中被重写的方法的访问权限更低;
  4. 声明为final的方法不能被重写;
  5. 声明为static的方法不能被重写,但是能够被再次声明;
  6. 构造方法不能被重写;
  7. 子类和父类在同一个包中,那么子类可以重写父类所有除了声明为private和final的方法;
  8. 如果不能继承一个方法,则不能重写这个方;
  9. 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法;
  10. 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重 写方法声明的更广泛的强制性异常,反之则可以。

注意事项

  • 私有方法:私有方法不能被重写(父类私有成员子类是不能继承的)。
  • 静态方法:父类中的静态方法不能被重写,如果子类中有相同的方法,它并不是重写了父类的方法,而是将父类同名的方法隐藏了起来。
  • 使用@Override注解:建议在子类中重写父类的方法时加上@Override注解,这可以提高代码的可读性,并且可以在编译阶段检查重写是否正确。

示例代码

以下是一个简单的示例,展示了方法重写的基本用法:

class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    // 重写父类的方法
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }

    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();  // 输出 "Dog is eating"
        
        // 多态性演示
        Animal myDog = new Dog();
        myDog.eat();  // 输出 "Dog is eating"
    }
}

在这个例子中,Dog类继承了Animal类,并重写了eat()方法。当我们通过Dog类型的引用调用eat()方法时,输出的是"Dog is eating",这体现了方法重写的多态性特征。

方法重写是Java中实现多态的一种方式,通过这种方法,可以使程序更加灵活,并且能够有效地扩展父类的功能,而不改变父类的实现。

注、方法重写与方法重载区别

  • 方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
  • 方法重写:子类可以有一个与父类签名完全相同的方法,即子类可以重写父类的方法。

十、在Java中,什么时候用重载,什么时候用重写?

(1)重载是多态的集中体现,在类中,要以统一的方式处理不同类型数据的时候,可以用重载。
(2)重写的使用是建立在继承关系上的,子类在继承父类的基础上,增加新的功能,可以用重写。
(3)简单总结:
1.重载是多样性,重写是增强剂;
2.目的是提高程序的多样性和健壮性,以适配不同场景使用时,使用重载进行扩展;
3.目的是在不修改原方法及源代码的基础上对方法进行扩展或增强时,使用重写;

十一.方法的组合(重点)

Java中的组合(Composition)是一种面向对象的设计模式,它允许将对象组合成树形结构来表示部分-整体层次关系。组合使用户能够一致地对待单个对象和对象的组合。下面将详细探讨Java组合的概念、实现步骤、示例代码、意义以及与继承的区别。

组合的概念

组合是指将一个类的对象作为另一个类的成员变量,这种关系被称为“has-a”的关系,表示一个类拥有另一个类的实例。组合模式允许新类直接复用该类的public方法,不破坏封装性,并且整体类与局部类之间松耦合,彼此相对独立且具有更好的可扩展性。

组合的实现步骤

在Java中实现组合主要包括以下几个步骤:

  1. 定义类和属性:定义将要组合的对象所在的类以及这些对象所包含的属性。
  2. 创建对象:实例化组合中的对象。
  3. 调用方法:在组合类中定义方法来调用成员对象的方法。
  4. 创建并调用组合对象:创建组合类的对象,并通过它来调用成员对象的方法。

示例代码

一、定义类和属性

首先,你需要定义一个类,这个类将作为新类的成员变量。例如,如果你需要创建一个表示汽车的类,你可能需要定义一个表示引擎的类,然后在汽车类中包含一个引擎的对象。

public class Engine {
    // 定义引擎的属性和方法
}
public class Car {
    private Engine engine; // Car类包含一个Engine对象
    // 定义汽车的其他属性和方法
}

二、创建对象

然后,你需要创建对象。你可以在创建新类的对象时,同时创建成员变量的对象。

Engine engine = new Engine();
Car car = new Car(engine);

三、调用方法

你可以通过新类的对象来调用成员变量对象的方法。

car.getEngine().start(); // 通过汽车对象来启动引擎

四、创建并调用组合对象

你也可以创建一个包含多个对象的新类,然后通过这个新类来调用各个对象的方法。

public class Car {
    private Engine engine;
    private Wheel wheel;
    private Seat seat;
    // 定义汽车的其他属性和方法
    public void start() {
        engine.start();
        wheel.rotate();
        seat.adjust();
    }
}
Engine engine = new Engine();
Wheel wheel = new Wheel();
Seat seat = new Seat();
Car car = new Car(engine, wheel, seat);
car.start(); // 启动汽车,将会依次启动引擎、旋转轮胎、调整座椅

完整代码:

// 定义Engine类
public class Engine {
    public void start() {
        System.out.println("Engine started.");
    }
}

// 定义Wheel类
public class Wheel {
    public void rotate() {
        System.out.println("Wheels are rotating.");
    }
}

// 定义Seat类
public class Seat {
    public void adjust() {
        System.out.println("Seat adjusted.");
    }
}

// 定义Car类,组合Engine, Wheel, Seat类
public class Car {
    private Engine engine;
    private Wheel wheel;
    private Seat seat;

    public Car(Engine engine, Wheel wheel, Seat seat) {
        this.engine = engine;
        this.wheel = wheel;
        this.seat = seat;
    }

    public void startCar() {
        engine.start();
        wheel.rotate();
        seat.adjust();
    }

    public Engine getEngine() {
        return engine;
    }
}

public class Main {
    public static void main(String[] args) {
        Engine engine = new Engine();
        Wheel wheel = new Wheel();
        Seat seat = new Seat();
        
        Car car = new Car(engine, wheel, seat);
        
        car.startCar(); // 启动汽车,将会依次启动引擎、旋转轮胎、调整座椅
        
        // 通过汽车对象来启动引擎
        car.getEngine().start();
    }
}

这段代码展示了如何在Java中使用组合来构建一个Car类,该类组合了EngineWheelSeat类的对象。以下是对代码的详细解释:

类定义

  1. Engine类

    • 代表汽车的引擎,有一个start方法,用于启动引擎。
  2. Wheel类

    • 代表汽车的轮胎,有一个rotate方法,用于轮胎旋转。
  3. Seat类

    • 代表汽车的座椅,有一个adjust方法,用于调整座椅。
  4. Car类

    • 这是一个组合类,它组合了EngineWheelSeat的对象。
    • Car类有一个构造函数,它接受EngineWheelSeat的对象作为参数,并初始化相应的成员变量。
    • startCar方法用于启动汽车,它依次调用enginewheelseatstartrotateadjust方法。
    • getEngine方法提供了一种方式来访问Car对象中的Engine对象。

主方法

  • main方法是程序的入口点。
  • 在这个方法中,首先创建了EngineWheelSeat的对象。
  • 然后使用这些对象创建了一个Car对象。
  • 调用car.startCar()来启动汽车,这将依次执行引擎启动、轮胎旋转和座椅调整。
  • 另外,还展示了如何通过Car对象的getEngine方法来访问并直接启动引擎。

输出

当运行这个程序时,控制台的输出将是:

Engine started.
Wheels are rotating.
Seat adjusted.
Engine started.

这表示汽车已经启动,包括引擎启动、轮胎旋转和座椅调整。第二次调用Enginestart方法是通过Car对象的getEngine方法实现的。

总结

这个示例展示了组合模式的典型用法,其中Car类组合了EngineWheelSeat类的对象来实现汽车的启动过程。组合模式允许Car类将这些对象作为整体来处理,同时提供了一种方式来访问和操作这些组合的对象。这种设计提高了代码的模块化和可维护性,并且使得Car类可以灵活地添加或替换组件。

组合的意义

组合的意义主要体现在以下几个方面:

  1. 代码复用:通过将对象作为成员变量,可以方便地重用已有的代码。
  2. 灵活性:组合允许在运行时改变对象的组合方式,从而增强了程序的灵活性。
  3. 易于维护:由于组合中的对象相对独立,因此当需要修改或扩展某一部分时,不会影响到其他部分。
  4. 提高可测试性:组合可以使得对象更加容易被单独测试,因为它们之间的依赖性较弱。

总结

组合模式在Java编程中提供了一种有效的方法来构建和表示层次化的结构,使得客户端可以一致地处理单个对象和组合对象。通过组合,可以构建出更加灵活、可复用的系统架构。同时,组合相较于继承,提供了更好的封装性和可扩展性,减少了类间的耦合度,使得代码更加模块化,更易于维护和扩展。

posted @   北方的尘中客  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示