Java基础:面向对象编程3

news2024/10/17 17:18:48

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); // 输出: 1
        System.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;
    }

    @Override
    protected 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对象创建之门的钥匙

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2216058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Zsh 安装与配置

目录 1 环境配置 1.1 基本工具安装 1.2 安装 oh-my-zsh 1.3 从.bashrc中迁移配置&#xff08;可选&#xff09; 2 主题配置 2.1 内置主题 2.2 自定义主题 2.2.1 推荐主题 3 插件安装 3.1 推荐插件 3.1.1 zsh -autosuggestions 3.1.2 zsh-syntax-highlighting 3.2 启…

kubernetes中的微服务

目录 一 什么是微服务 二 微服务的类型 三 ipvs模式 3.1 ipvs模式配置方式 四 微服务类型详解 4.1 clusterip 4.2 ClusterIP中的特殊模式headless 4.3 nodeport 4.4 loadbalancer 4.5 metalLB 4.6 externalname 五 Ingress-nginx 5.1 ingress-nginx功能 5.2 部署…

【数据结构】1.顺序表

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C &#x1f308;喜欢的诗句:天行健,君子以自强不息. 线性表 线性表&#xff08;List&#xff09;&#xff1a;零个或多个数据元素的有限序列…

软考(网工)——数据通信基础

&#x1f550;信道特性 1️⃣概念 通信得目的就是传递信息。通信中产生和发送信息得一端叫信源&#xff0c;接受信息的一段叫信宿&#xff0c;信源和信宿之间的通信线路称为信道。 2️⃣信道带宽 W 模拟信道&#xff1a;Wf2-f1&#xff08;f2 和 f1分别表示&#xff1a;信道…

树的中心——dfs

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510, M N*2; int h[N], e[M], ne[M], idx; int n; int ans 2e9; bool st[N]; void add(int a, int b) // 添加一条边a->b {e[idx] b, ne[idx] h[a], h[a] idx ; } int dfs(int u) {int…

芯片记录一下

1、MC34063 电源管理DCDC 输入电压&#xff1a;-0.3~40V 输出电压&#xff1a;Vout1.25*&#xff08;1R2/R1&#xff09; 1.25V~40V

【报错解决】安装scikit-rebate包报错

scikit-rebate ReBATE是一套基于Relief的机器学习特征选择算法 报错信息 解决方案 conda install numpy scipy scikit-learnpip install skrebate依次运行以上两步&#xff0c;即可成功安装&#xff01;

如何实时监测你的光纤资源?

光纤资源作为重要的通信基础设施&#xff0c;实时监测光纤资源的状态是运营管理好光纤资源的重要手段&#xff0c;那常用的监测指标维度与方法有那些呢&#xff1f; 维度1&#xff1a;资源数量 资源数量主是建立资源的基础档案&#xff0c;掌握光缆的型号、路由&#xff1b;光…

健康推荐系统:SpringBoot技术实现

3系统分析 3.1可行性分析 通过对本基于智能推荐的卫生健康系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于智能推荐的卫生健康系统采用SSM框架&#…

python 位运算 笔记

起因&#xff0c; 目的: 位运算&#xff0c;令我头疼的地方。算法题里面也是经常见到。 位运算。 按位或&#xff0c;OR, | , 只要有一个为1&#xff0c; 结果就是1&#xff0c;否则为0按位异或&#xff0c;XOR, ^, 2个数不同&#xff0c;结果为1&#xff0c; 否则为0&#…

k8s jenkins 2.421动态创建slave

k8s jenkins 动态创建slave 简述使用jenkins动态slave的优势&#xff1a;制作jenkins-slave从节点jenkins-slave-latest:v1配置jenkins动态slave配置 Pod Template配置容器模板挂载卷 测试 简述 持续构建与发布是我们日常工作中必不可少的一个步骤&#xff0c;目前大多公司都采…

《OpenCV计算机视觉》—— 使用DNN模块实现图片风格迁移

文章目录 OpenCV中的DNN模块一、功能概述二、支持的模型格式三、基本使用方法四、DNN 模块的特点五、常见应用示例 示例&#xff1a;图片风格迁移 OpenCV中的DNN模块 OpenCV中的DNN&#xff08;Deep Neural Network&#xff09;模块是一个功能强大的工具&#xff0c;它允许开发…

python pip安装requirements.txt依赖与国内镜像

python pip安装requirements.txt依赖与国内镜像 如果网络通畅&#xff0c;直接pip安装依赖&#xff1a; pip install -r requirements.txt 如果需要国内的镜像&#xff0c;可以考虑使用阿里的&#xff0c;在后面加上&#xff1a; -i http://mirrors.aliyun.com/pypi/simple --…

Linux--多路转接之epoll

上一篇:Linux–多路转接之select epoll epoll 是 Linux 下多路复用 I/O 接口 select/poll 的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU 利用率。它是 Linux 下多路复用 API 的一个选择&#xff0c;相比 select 和 poll&#xff0c…

自建 Bitwarden 密码管理器

大佬零度解说的文件修改,与原文不太一样,详细请看&#xff1a;自建 Bitwarden 密码管理器&#xff01;完全免费开源&#xff0c;轻量级&#xff0c;安全又可靠&#xff01;-零度解说 教程&#xff1a;你的密码真的安全吗&#xff1f;Bitwarden 密码管理器&#xff01;一键部署…

【Redis】缓存预热、雪崩、击穿、穿透、过期删除策略、内存淘汰策略

Redis常见问题总结&#xff1a; Redis常见问题总结Redis缓存预热Redis缓存雪崩Redis缓存击穿Redis缓存穿透 Redis 中 key 的过期删除策略数据删除策略 Redis内存淘汰策略一、Redis对过期数据的处理&#xff08;一&#xff09;相关配置&#xff08;二&#xff09;内存淘汰流程&a…

el-table表格里面有一条横线

表格里面 有一条横线&#xff0c; 出现原因&#xff1a;是自定义了表格头.使用了固定列&#xff08;fixed&#xff09;&#xff0c;定宽。就很难受。。。 添加样式文件&#xff1a; <style lang"scss" scoped>::v-deep {.el-table__fixed-right {height: 100%…

植物大战僵尸杂交版之后要出联机版植物大战僵尸?(内测中,可在安卓手机上玩,文末附下载链接)

继植物大战僵尸杂交版之后给大家介绍一个杂交版作者正在酝酿的“植物大战僵尸射击版” 植物大战僵尸射击版介绍 《植物大战僵尸杂交版》的创作者“潜艇伟伟迷”即将推出PVZ改版新作——《植物大战僵尸射击版》。游戏将支持PC、手游和web端&#xff0c;提供单人、双人、三人、…

取证之FTK Imager学习笔记

一、FTK Imager制作镜像详细教程 1、文件-创建磁盘镜像 2、参数详解&#xff1a; 1&#xff09;物理驱动器 整个驱动器&#xff0c;如&#xff1a;识别到的是整块硬盘、U盘等&#xff0c;而不管你分几个分区&#xff1b; 2&#xff09;逻辑驱动器&#xff08;L&#xff09…

数据结构-5.9.树的存储结构

一.树的逻辑结构&#xff1a; 二.双亲表示法(顺序存储)&#xff1a; 1.树中除了根结点外每一颗树中的任意一个结点都只有一个父结点(双亲结点)&#xff1b; 2.结点包括结点数据和指针&#xff1b; 3.上述图片中右边的顺序存储解析&#xff1a;比如A结点左边的0&#xff0c;就…