欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > Java基础:面向对象编程3

Java基础:面向对象编程3

2024/11/7 6:26:33 来源:https://blog.csdn.net/gaosw0521/article/details/142908634  浏览:    关键词:Java基础:面向对象编程3

1 Java可变长参数

1.1 概述

Java 的可变长参数(Varargs)是在 Java 1.5 中引入的功能,允许方法接受任意数量的相同类型的参数。可变参数的语法是在参数类型后面加上三个点(...),例如 int... numbers

1.2 使用规则

  • 可变参数必须放在参数列表的最后一位:如果方法有多个参数,可变参数必须放在最后。例如:
    public void printNumbers(String prefix, int... numbers) {// 方法体
    }
    
  • 只能有一个可变参数:一个方法中只能有一个可变参数。

1.3 原理

当使用可变参数时,Java 会在编译时创建一个数组,数组的大小就是传入的可变参数的数量。然后将这些参数放入数组中,并将数组传递给方法。

例如:

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

调用 printNumbers(1, 2, 3) 时,Java 会创建一个 int[] 数组 {1, 2, 3},然后传递给方法。

1.4 使用场景

可变参数通常用于需要处理任意数量相同类型对象的场景。例如:

  • 打印任意数量的整数
  • 计算任意数量的数字的和
  • 处理任意数量的字符串

1.5 注意事项

  • 避免重载带有可变参数的方法:重载带有可变参数的方法可能会导致编译器无法确定调用哪个方法,从而引发编译错误。例如:

    public void print(String... args) {// 方法体
    }public void print(String arg1, String... args) {// 方法体
    }
    

    调用 print("a") 时,编译器无法确定是调用第一个方法还是第二个方法。

  • 明确指示:如果必须重载带有可变参数的方法,确保在调用时明确指示参数,避免编译器混淆。

1.6 示例代码

public class VarargsExample {public static void main(String[] args) {printNumbers(1, 2, 3);printNumbers(4, 5);printNumbers();}public static void printNumbers(int... numbers) {for (int number : numbers) {System.out.println(number);}}
}

2 Java native方法

2.1 概念

本地方法(Native Method)是用 native 关键字修饰的方法,通常不需要用 Java 语言实现。本地方法允许 Java 代码调用其他语言(如 C/C++)编写的代码。

2.2 JNI:Java Native Interface

JNI(Java Native Interface)是 Java 平台的一部分,从 Java 1.1 开始引入,允许 Java 代码与其他语言编写的代码进行交互。JNI 主要用于以下场景:

  • 标准 Java 类库不支持的功能
  • 调用已有的 C/C++ 库
  • 提高性能,特别是在需要接近硬件或运行次数特别多的方法中

2.2.1 JNI 的优点

  • 扩展 Java 功能:可以通过 JNI 调用其他语言编写的库。
  • 性能优化:在某些情况下,使用本地代码可以提高性能。

2.2.2 JNI 的缺点

  • 跨平台性丧失:本地代码通常不跨平台,需要在不同系统环境下重新编译。
  • 安全性降低:本地代码的不当使用可能导致程序崩溃。

2.3 用 C 语言编写本地方法

2.3.1 步骤

  1. 编写带有 native 方法的 Java 类,生成 .java 文件。
  2. 编译 Java 类,生成 .class 文件。
  3. 生成头文件:使用 javah -jnijavac -h 生成 .h 文件。
  4. 实现本地方法:使用 C/C++ 实现 .h 文件中的方法,生成 .c.cpp 文件。
  5. 生成动态链接库:将 C/C++ 编写的文件生成动态链接库(如 .dll.so)。

2.3.2 示例:HelloWorld 程序

  1. 编写 Java 类

    public class HelloJNI {static {System.loadLibrary("hello"); // 加载动态链接库}public native void sayHello(); // 声明本地方法public static void main(String[] args) {new HelloJNI().sayHello(); // 调用本地方法}
    }
    
  2. 编译 Java 类

    javac HelloJNI.java
    
  3. 生成头文件

    javac -h . HelloJNI.java
    

    这将生成 HelloJNI.h 文件。

  4. 实现本地方法

    #include <jni.h>
    #include "HelloJNI.h"
    #include <stdio.h>JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj) {printf("Hello, JNI!\n");
    }
    
  5. 编写编译脚本

    # compile.sh
    gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" -dynamiclib -o libhello.dylib HelloJNI.c
    
  6. 执行脚本

    sh compile.sh
    
  7. 运行 Java 程序

    java HelloJNI
    

2.4 JNI 调用 C 的流程图

Java 代码 -> JNI 接口 -> 本地代码 (C/C++)

在这里插入图片描述

2.5 native 关键字

  • native 用于修饰方法,表示该方法的实现在外部定义,通常用 C/C++ 实现。

  • 语法

    • 修饰方法的位置必须在返回类型之前。
    • 不能用 abstract 修饰,没有方法体。
    • 返回值可以是任意类型。
  • native 方法示例

public native void sayHello();

2.6 小结

  • JNI 允许 Java 代码与其他语言编写的代码进行交互,扩展了 Java 的功能。
  • 本地方法native 关键字修饰,通常用于调用 C/C++ 库或提高性能。
  • 注意事项:使用 JNI 会丧失跨平台性,且本地代码的不当使用可能导致程序崩溃。

3 Java构造方法

3.1 概念

构造方法(Constructor)是 Java 中的一种特殊方法,用于在创建对象时初始化对象的状态。每次使用 new 关键字创建对象时,构造方法至少会被调用一次。如果没有显式定义构造方法,编译器会提供一个默认的无参构造方法。

3.2 创建构造方法的规则

  • 名称与类名相同:构造方法的名称必须与类名完全一致。
  • 无返回类型:构造方法没有返回类型,包括 void
  • 不能是抽象的、静态的、最终的、同步的
    • 抽象:构造方法不能被子类继承,因此用 abstract 修饰没有意义。
    • 静态:构造方法用于初始化对象,因此用 static 修饰没有意义。
    • 最终:构造方法不能被子类继承,因此用 final 修饰没有意义。
    • 同步:多个线程不会同时创建内存地址相同的同一个对象,因此用 synchronized 修饰没有必要。

3.3 语法格式

class class_name {public class_name(){}    // 默认无参构造方法public ciass_name([paramList]){}    // 定义有参数列表的构造方法// 类主体
}

3.4 默认构造方法

  • 无参构造方法:如果构造方法没有任何参数,它就是一个无参构造方法。
  • 默认构造方法:如果类中没有显式定义构造方法,编译器会自动提供一个无参构造方法。
  • 目的:主要为对象的字段提供默认值。
  • 代码示例
public class DefaultConstructorExample {private int value;// 编译器提供的默认构造方法public DefaultConstructorExample() {this.value = 0; // 默认值}public static void main(String[] args) {DefaultConstructorExample obj = new DefaultConstructorExample();System.out.println(obj.value); // 输出: 0}
}

3.5 有参构造方法

  • 有参数的构造方法:可以有一个或多个参数,用于为不同的对象提供不同的初始值。
  • 替代方案:如果没有有参构造方法,可以通过 setter 方法为字段赋值。
  • 代码示例
public class ParameterizedConstructorExample {private String name;public ParameterizedConstructorExample(String name) {this.name = name;}public static void main(String[] args) {ParameterizedConstructorExample obj = new ParameterizedConstructorExample("Java");System.out.println(obj.name); // 输出: Java}
}

3.6 重载构造方法

  • 构造方法重载:通过提供不同的参数列表来重载构造方法。编译器会根据参数的数量和类型来决定调用哪一个构造方法。
  • 代码示例
public class OverloadedConstructorExample {private int id;private String name;public OverloadedConstructorExample(int id) {this.id = id;}public OverloadedConstructorExample(int id, String name) {this.id = id;this.name = name;}public static void main(String[] args) {OverloadedConstructorExample obj1 = new OverloadedConstructorExample(1);OverloadedConstructorExample obj2 = new OverloadedConstructorExample(2, "Java");System.out.println(obj1.id); // 输出: 1System.out.println(obj2.id + " " + obj2.name); // 输出: 2 Java}
}

3.7 构造方法和方法的区别

特性方法构造方法
目的反映对象的行为初始化对象的字段
返回类型可以有返回类型没有返回类型
调用方式明确的,开发者通过代码决定调用隐式的,通过编译器完成
编译器提供不会由编译器提供如果没有明确提供无参构造方法,编译器会提供
名称可以和类名相同,也可以不同必须和类名相同

3.8 复制对象

复制一个对象可以通过以下三种方式完成:

  1. 通过构造方法:使用另一个对象作为参数来创建新对象。
  2. 通过对象的值:手动复制对象的每个字段。
  3. 通过 Object 类的 clone() 方法:实现 Cloneable 接口并重写 clone() 方法。
  • 通过构造方法
public class CopyConstrutorPerson {private String name;private int age;public CopyConstrutorPerson(String name, int age) {this.name = name;this.age = age;}public CopyConstrutorPerson(CopyConstrutorPerson person) {this.name = person.name;this.age = person.age;}public void out() {System.out.println("姓名 " + name + " 年龄 " + age);}public static void main(String[] args) {CopyConstrutorPerson p1 = new CopyConstrutorPerson("沉默王二",18);p1.out();CopyConstrutorPerson p2 = new CopyConstrutorPerson(p1);p2.out();}
}

在上面的例子中,有一个参数为 CopyConstrutorPerson 的构造方法,可以把该参数的字段直接复制到新的对象中,这样的话,就可以在 new 关键字创建新对象的时候把之前的 p1 对象传递过去。

  • 通过对象的值
public class CopyValuePerson {private String name;private int age;public CopyValuePerson(String name, int age) {this.name = name;this.age = age;}public CopyValuePerson() {}public void out() {System.out.println("姓名 " + name + " 年龄 " + age);}public static void main(String[] args) {CopyValuePerson p1 = new CopyValuePerson("沉默王二",18);p1.out();CopyValuePerson p2 = new CopyValuePerson();p2.name = p1.name;p2.age = p1.age;p2.out();}
}

直接拿 p1 的字段值复制给 p2 对象(p2.name = p1.name)

  • 通过 Object 类的 clone() 方法
public class ClonePerson implements Cloneable {private String name;private int age;public ClonePerson(String name, int age) {this.name = name;this.age = age;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public void out() {System.out.println("姓名 " + name + " 年龄 " + age);}public static void main(String[] args) throws CloneNotSupportedException {ClonePerson p1 = new ClonePerson("沉默王二",18);p1.out();ClonePerson p2 = (ClonePerson) p1.clone();p2.out();}
}

通过 clone() 方法复制对象的时候,ClonePerson 必须先实现 Cloneable 接口的 clone() 方法,然后再调用clone()方法(ClonePerson p2 = (ClonePerson) p1.clone())

3.9 小结

  • 构造方法虽然没有返回值,但返回的是类的对象
  • 初始化字段只是构造方法的一种工作,它还可以做更多,比如启动线程、调用其他方法等。

4 思维导图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

参考链接

  1. Java可变参数详解,5分钟教会我妹

  2. 手把手教你用 C语言实现 Java native 本地方法

  3. Java构造方法:打开Java对象创建之门的钥匙

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com