-
🍊数据类型 在Java中数据类型主要分为两类:基本数据类型和引用数据类型。本文来介绍基本数据类型。 基本数据类型有:整型、浮点型、字符型以及布尔型 (这看起来和C语言一样,还挺亲切的) 🍌整型 Java中的整型有:byte,short,int,long 与C语言相比,short和int没啥变化。 有变化的点在于,多了一个byte,叫做字节型,它占一个字节,很像C语言中的char,它的范围也是从-128~127。(待会下面会对这个范围作一些说明) 同时我们不难发现,Java没有long long,因为它的long(长整型)的大小就是8个字节了,不像C语言的long有时是4个字节,而有时是8。这也正引出一个重要的点:Java的数据类型在32位和64位环境下的大小都是一样的,这保证了在不同平台的可移植性。 🥝byte的范围 (其实我之前学C语言的时候char的取值范围没有完全弄懂hhh) 我们以前学过:数据在内存中的存储是补码。数据的二进制序列中最高位是符号位。因为C语言和Java只是语言不同而已,所以它们在数据存储方面其实是大同小异的。 Java中的基本数据类型没有“无符号”这一概念,也就是说都是有符号的,必定有符号位。 先来看正数的情况,易知只要数值位都是1就可以取到最大值127。 负数的情况有点小复杂,按理来说负数最小值是-127,即11111111。至于-128,其实是人为规定的,怎么规定呢?127的源码和1的源码相加就是1000 0000,规定这个数就是-128(就最高位的1同时作为数值位和符号位这样子) 然后还有两件事: 第一,我们判定一个数的大小(这里指的是十进制大小)可以看源码,也可以看补码,但是看源码会比较直观,因为你可以直接算出来。 第二,计算机中各种运算主要是以补码参与运算。 🍌浮点型 Java浮点型也是float和double。float大小为4个字节;double为8个字节。 我们在写代码的时候如果写 float a = 0.5; 1 那编译器会报错,因为Java中浮点数默认是double类型的,而a是float,自然报错了(而C语言就不会这样,这也体现了Java的严谨)。如果你要让a就是float类型的话,就在后面加个f: float a = 0.5f; 1 🥝浮点数相关运算 这部分的规则也是和C语言的差不多,在此不多赘述(其实是我懒得写 )。但仍要补充一些要点: ①对于整型的除法运算,若想转化为浮点数的除法运算,则在分子or分母乘0.1就ok了。 ②Java中的小数并没有精确的位数,哪怕你在初始化一个浮点数变量时给它一个有限位数的初始值,比如float a = 1.1,是两位小数,但是你去打印a*a,会发现结果不是1.21。 🍊字符类型 计算机中的字符本质上是一个整数。 C 语言中使用 ASCII 表示字符,而 Java 中使用 Unicode 表示字符。因此一个字符占用两个字节,表示的字符种类更多,包括中文。 char c1 = 'A'; // 大写字母 char c2 = '1'; // 数字字符 System.out.println(c1); System.out.println(c2); char c3 = '帅'; System.out.println(c3); 1 2 3 4 5 6 7 8 这里先简单介绍一下,后续会有专门的章节来讲字符和字符串的。 🍊布尔类型 布尔类型(boolean)常用来表示真假,它只有两种取值,true 表示真,false 表示假。 Java 的 boolean 类型和 int 不能相互转换,不存在1表示 true,0表示false这样的用法!!! 然后值得注意的是,Java虚拟机规范中,并没有明确规定boolean占几个字节。 🍊类型转换 (Java 作为一门强类型编程语言,当不同类型之间的变量相互赋值的时候,会有比较严格的校验。) 在Java中,当参与运算数据类型不一致时,就会进行类型转换。Java中类型转换主要分为两类:自动类型转换(隐式)和强制类型转换(显式)。 🍌自动类型转换(隐式) 代码不需要经过任何处理,在代码编译时,编译器会自动进行处理。 特点:数据范围小的转为数据范围大的时会自动进行。 int a = 100; long b = 10L; b = a; // a和b都是整形,a的范围小,b的范围大,当将a赋值给b时,编译器会自动将a提升为long类型,然后赋值 a = b; // 编译报错,long的范围比int范围大,会有数据丢失,不安全 1 2 3 4 ❓然后不知道你有没有发现这样一个问题,为啥我把byte或short赋为整型值的时候编译器不会报错呢?比如byte = 11; short = 20。 因为Java规定:对于byte,short这两种大小小于4个字节的类型,你对这两种类型的变量进行赋值的时候,只要赋的值的大小不超过这个类型的范围,那么这个值就不会被解析为整型。 比如byte a = 300,因为300已经超过byte的范围,那么此时300就会被当做整型,就会报错了。 🍌强制类型转换(显式) 这个和C语言的差不多,打个比方,强制类型转换就像你现在有一条2米的杆子,你要放进一个1米长的箱子,就只能把它砍短咯。举一些例子让你回忆一下: int a = 10; long b = 100L; b = a; // int-->long,数据范围由小到大,隐式转换 a = (int)b; // long-->int, 数据范围由大到小,需要强转,否则编译失败 float f = 3.14F; double d = 5.12; d = f; // float-->double,数据范围由小到大,隐式转换 f = (float)d; // double-->float, 数据范围由大到小,需要强转,否则编译失败 a = d; // 报错,类型不兼容 a = (int)d; // int没有double表示的数据范围大,需要强转,小数点之后全部丢弃 byte b1 = 100; // 100默认为int,没有超过byte范围,隐式转换 byte b2 = (byte)257; // 257默认为int,超过byte范围,需要显示转换,否则报错 boolean flag = true; a = flag; // 编译失败:类型不兼容 flag = a; // 编译失败:类型不兼容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 总结一下就是: 不同数字类型的变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型 如果需要把范围大的类型赋值给范围小的, 需要强制类型转换, 但是可能精度丢失 将一个字面值常量进行赋值的时候, Java 会自动针对数字范围进行检查 强制类型转换不一定能成功,不相干的类型不能互相转换 🍊类型提升 不同类型的数据之间相互运算时,数据类型小的会被提升到数据类型大的。 ①int与long之间:int会被提升为long int a = 10; long b = 20; int c = a + b; // 编译出错: a + b==>int + long--> long + long 赋值给int时会丢失数据 long d = a + b; // 编译成功:a + b==>int + long--->long + long 赋值给long 1 2 3 4 第三行代码中,a类型提升之后变为long类型,此时long+long结果为long,不能用int类型接收。 ②byte与byte的运算 byte a = 10; byte b = 20; byte c = a + b; System.out.println(c); 1 2 3 4 这段代码编译也会报错,a和b虽然都是byte,但是计算 a + b 会先将 a和b 都提升成 int,再进行计算,得到的结果也是 int,此时赋给c,就会出错。 byte和short这种低于4个字节的类型,会先提升成 int,再参与计算。 原因:计算机的 CPU 通常是按照4个字节为单位从内存中读写数据,为了硬件上实现方便,所以在计算时把它们给提升了。 🍡写在最后 如果你觉得本文写得还不错,不妨点赞收藏关注三连呗! 如果发现本文有不足或不妥之处,欢迎指出,我将第一时间改正。 ———————————————— 版权声明:本文为CSDN博主「Ice_Sugar_7」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Ice_Sugar_7/article/details/134015650
-
一、实验目的 本实验的目的是让学生使用类来封装对象的属性和功能。 三、实验要求 编写一个简单的Java应用程序,该程序有两个类:Vehicle(用于刻画机动车)和User(主类)。具体要求如下: Vehicle类有个double类型的变量speed,用于刻画机动车的速度,一个int型变量power,用于刻画机动车的功率。类中定义了speedUp(int s)方法,体现机动车有加速功能;定义了speedDown(int d)方法,体现机动车有减速功能;定义了setPower(int p)方法,用于设置机动车的功率;定义了getPower()方法,用于获取机动车的功率。机动车的UML图如图4.2所示。 在主类User的 main()方法中用Vehicle为创建对象,并让该对象调用方法设置功率,演示加速和减速功能。 四、程序效果示例 程序运行效果如图4.3所示。 图4.3 Vehicle类创建对象 运行结果如下: 五、程序模板 请按模板要求,将【代码】替换为Java程序代码。 【Vehicle.java】 public class Vehicle { double speed;//声明double型变量speed,刻画速度 int power; //声明int型变量power,刻画功率 void speedUp(int s) { speed = s+speed;//将参数s的值与成员变量speed的和赋值给成员变量speed } void speedDown(int d) { speed= speed-d; //将成员变量speed与参数d的差赋值给成员变量speed } void setPower(int p) { power = p; //将参数p的值赋值给成员变量power } int getPower() { return power; //返回成员变量power的值 } double getSpeed() { return speed; } } 【User.java】 public class User { public static void main(String args[]) { Vehicle car1, car2; car1 = new Vehicle(); //使用new运算符和默认的构造方法创建对象car1 car2 = new Vehicle();//使用new运算符和默认的构造方法创建对象car2 car1.setPower(128); car2.setPower(76); System.out.println("car1的功率是:"+car1.getPower()); System.out.println("car2的功率是:"+car2.getPower()); car1.speedUp(80);//car1调用speedUp()方法将自己的speed的值增加80 car2.speedUp(80);//car2调用speedUp()方法将自己的speed的值增加80 System.out.println("car1目前的速度:"+car1.getSpeed()); System.out.println("car2目前的速度:"+car2.getSpeed()); car1.speedDown(10); car2.speedDown(20); System.out.println("car1目前的速度:"+car1.getSpeed()); System.out.println("car2目前的速度:"+car2.getSpeed()); } } 五、实验后的练习 (1)改进speedUp()方法,使得Vehicle类的对象在加速时不能将speed值超过200。 void speedUp(int s) { if (s+speed > 200){ this.speed = 200; }else { this.speed = s+speed; };//将参数s的值与成员变量speed的和赋值给成员变量speed } 2. 改进speedDown()方法,使得Vehicle类的对象在减速时不能将speed值小于0。 (4)void speedDown(int d) { if(speed-d < 0){ this.speed=0; } else { this.speed= speed-d; //将成员变量speed与参数d的差赋值给成员变量speed } } (3)增加一个刹车方法void brake(),Vehicle类的对象调用它能将speed的值变成0。 void brake(){ this.speed = 0; } 调用brake: car1.brake(); System.out.println("car1目前的速度:"+car1.getSpeed()); 结果如下: ———————————————— 版权声明:本文为CSDN博主「刘涧茗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/sziit1609030127/article/details/129509725
-
近期遇到一个问题,之前发布的APP连接蓝牙都是正常的,现在有人反映连不上了。经过测试发现:android 12 和 harmonyOS 3.0.0 都会有这个问题,而之前的版本就不会有这个。 经过网上一番查找,原来是因为最近Google发布的Android 12,新引入了 BLUETOOTH_SCAN、BLUETOOTH_CONNECT、BLUETOOTH_ADVERTISE 三个权限。、 从Android 12开始,过去的蓝牙权限被拆分成了3个新的权限,并且全都是运行时权限(需要动态申请): BLUETOOTH_SCAN 用于使用蓝牙扫描附件其他的蓝牙设备 BLUETOOTH_ADVERTISE 用于允许当前的设备被其他的蓝牙设备所发现 BLUETOOTH_CONNECT 用于连接之前已经配对过的蓝牙设备 这3个权限都是从Android 12系统才开始有的,所以为了能够兼容过去的老版本,建议在AndroidManifest.xml中这样声明: <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> 新增的3个蓝牙权限都是运行时权限,因此只在AndroidManifest.xml中声明是没有用的,还要在代码中动态申请权限才行。必须先在应用中用户明确批准使用,然后才能查找蓝牙设备、使某个设备可被其他设备检测到,或者与已配对的蓝牙设备通信。 具体的申请方法如下:首先要判断当前的系统版本,只有当Android 12及以上系统时,才应该去请求新增的蓝牙权限。(PS:3个权限都属于同一个权限组,因此理论上只要申请一个权限,另外2个也就自动授权了。) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { String[] permission = checkSelfPermissionArray(this, new String[]{ Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT}); if (permission.length > 0) { ActivityCompat.requestPermissions(this, permission, 102); } } 注意: 之前的Android系统中有一个很奇怪的现象,当我们在应用中使用蓝牙扫描附件设备的时候,需要申请地理位置权限。蓝牙权限并不是运行时权限,但地理位置权限却是。 ———————————————— 版权声明:本文为CSDN博主「老杜_d」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/minusn/article/details/128660803
-
定义能计算圆的面积、周长的类 Circle,半径r为私有数据变量,其值由类circle的构造方法初始化,类circle提供读取半径r的方法getr();t算面积的方法area();计算周长的方法circlelength()。再定义类Circlecomputer,其包含程序运行所需的方法main,请设计程序完成初始化数据变量和计算圆的面积、周长并打印出结果。 以下是Java代码实现: public class Circle { private double r; public Circle(double r) { this.r = r; } public double getr() { return r; } public double area() { return Math.PI * r * r; } public double circlelength() { return 2 * Math.PI * r; } } public class Circlecomputer { public static void main(String[] args) { Circle circle = new Circle(3.0); // 初始化圆的半径为3.0 System.out.println("半径为:" + circle.getr()); System.out.println("面积为:" + circle.area()); System.out.println("周长为:" + circle.circlelength()); } } 在Circle类中,我们定义了私有数据变量r,通过构造方法初始化,并提供了读取半径r的方法getr()、计算面积的方法area()和计算周长的方法circlelength()。 在Circlecomputer类中,我们创建一个Circle对象并将半径初始化为3.0,然后调用Circle对象的方法计算面积和周长,并打印输出结果。 ———————————————— 版权声明:本文为CSDN博主「鱼弦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/feng1790291543/article/details/131311635
-
我想问一下CodeArts IDE for Java这个怎么在一个窗口里面打开多个工程。还有就是怎么能默认maven的配置,现在每次打开工程都要重新配置maven。
-
Java实现流媒体服务器的方案随着互联网的快速发展,流媒体技术在音视频领域得到了广泛的应用。流媒体服务器作为流媒体技术的核心组成部分,可以将音视频数据实时传输给用户。本文将介绍如何使用Java实现一个流媒体服务器的方案。选择合适的流媒体框架Java中有许多成熟的流媒体框架可供选择,如Red5、Darwin Streaming Server、JWPlayer等。这些框架提供了丰富的功能和良好的性能,可以帮助我们快速搭建流媒体服务器。本文将以Red5为例,介绍如何实现一个流媒体服务器。安装和配置Red5首先,我们需要下载并安装Red5服务器。可以从官方网站下载最新版本的Red5。安装过程非常简单,只需解压缩下载的文件,然后运行其中的red5.bat(Windows系统)或run.sh(Linux系统)即可启动Red5服务器。创建流媒体应用接下来,我们需要创建一个流媒体应用。在Red5中,流媒体应用是通过扩展名为.r303的配置文件来定义的。我们可以使用文本编辑器创建一个名为mystream.r303的文件,然后添加以下内容:<?xml version="1.0" encoding="UTF-8"?> <configuration> <node-list> <node name="mystream" description="My Stream Application" type="org.red5.server.stream.Application"> <param name="playback" value="false"/> <param name="record" value="false"/> <param name="preview" value="true"/> <param name="publish" value="true"/> <param name="application" value="mystream"/> <param name="name" value="mystream"/> <param name="autoStart" value="true"/> <param name="bufferTime" value="4000"/> <param name="maxBandwidth" value="1000000"/> <param name="minBandwidth" value="10000"/> <param name="audioBitrate" value="96"/> <param name="videoBitrate" value="128"/> <param name="audioSampleRate" value="44100"/> <param name="videoFrameRate" value="30"/> <param name="keyframeInterval" value="25"/> <param name="metadataInterval" value="25"/> <param name="segmentLength" value="3600"/> <param name="segmentGap" value="60"/> <param name="location" value="/mystream"/> </node> </node-list> </configuration>这个配置文件定义了一个名为mystream的流媒体应用,设置了各种参数,如音频和视频比特率、帧率等。保存文件后,将其放置在Red5服务器的配置目录下(默认为conf)。编写Java代码处理流媒体数据为了处理流媒体数据,我们需要编写一个Java类,继承自org.red5.server.stream.ClientBroadcastStream类。在这个类中,我们可以重写onPublish方法,用于处理推流事件;重写onPlay方法,用于处理播放事件;重写其他方法,以处理其他事件。以下是一个简单的示例:package com.example; import org.red5.server.api.stream.ClientBroadcastStream; import org.red5.server.api.stream.IStreamPacket; import org.red5.server.stream.ClientBroadcastStream; import org.red5.server.stream.scope.IScope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MyStream extends ClientBroadcastStream { private static final Logger log = LoggerFactory.getLogger(MyStream.class); private IScope scope; private String streamName; private int audioChannels; private int audioSampleRate; private int audioBitRate; private int videoWidth; private int videoHeight; private int videoCodecId; private boolean isAudioActive; private boolean isVideoActive; private long lastAudioReceived; private long lastVideoReceived; private long lastAudioSent; private long lastVideoSent; private long lastAudioKeyFrameSent; private long lastVideoKeyFrameSent; private long lastMetadataSent; private long segmentStartTime; private long segmentDuration; private boolean isRecording; private boolean isPreviewing; // ...其他成员变量和方法... }在这个类中,我们可以处理各种事件,如接收到音频和视频数据、连接断开等。同时,我们还可以根据需要添加其他功能,如录制、预览等。
-
SpringBoot有几种获取Request对象的方法_CodeArts_华为云论坛 (huaweicloud.com)
-
ThreadPoolExcecutor构造方法参数 1.继承关系 Executor ↑ ExecutorService ↑ AbstractExecutorService ↑ ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize,int maxNumPoolSize, long ReepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectExecutionHandler handler){ } corePoolSize:线程池核心线程数 maxNumPoolSize:线程池最大数 ReepAliveTime:空闲线程存活时间 unit:时间单位 workQueue:线程池所使用的缓冲队列 threadFactory:线程池创建使用的工厂 handler:线程池对拒绝任务的处理策略 特性一:当池中正在运行的线程数(包括空闲线程),小于corePoolSize时,从线程池中取线程执行任务。 特性二:当池中正在运行的线程数大于等于corePoolSize时,新插入的任务进入workQueue排队。 特性三:当队列里的任务达到上限,并且池中正在运行的线程数小于maxNumPoolSize,对于新加入的任务,新建线程。 特性四:当队列里的任务数达到上限,并且池中正在运行的线程数等于maxNumPoolSize,对于新加入的任务,执行拒绝策略(默认策略跑出RejectdExcutionException) ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/105604858
-
为了减少创建和销毁线程的次数,让每个线程可以多次使用,可根据系统情况调整执行的线程数量,防止消耗过多内存,所以我们可以使用线程池. Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 (1) newCachedThreadPool 创建一个可缓存线程池,如果线线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,若无可回收,则新建线程。示例代码如下: Java代码 package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { public void run() { System.out.println(index); } }); } } } 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。 (2) newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下: Java代码 package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。 定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors() (3) newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下: Java代码 package test; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorTest { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.schedule(new Runnable() { public void run() { System.out.println("delay 3 seconds"); } }, 3, TimeUnit.SECONDS); } } 表示延迟3秒执行。 定期执行示例代码如下: Java代码 package test; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorTest { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.scheduleAtFixedRate(new Runnable() { public void run() { System.out.println("delay 1 seconds, and excute every 3 seconds"); } }, 1, 3, TimeUnit.SECONDS); } } 表示延迟1秒后每3秒执行一次。 (4) newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下: Java代码 package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } 结果依次输出,相当于顺序执行各个任务。 ExcutorService中的excutor和submit方法的区别 两者都是将一个线程任务添加到线程池中并执行; 1、excutor没有返回值,submit有返回值,并且返回执行结果Future对象 2、excutor不能提交Callable任务,只能提交Runnable任务,submit两者任务都可以提交 3、在submit中提交Runnable任务,会返回执行结果Future对象,但是Future调用get方法将返回null(Runnable没有返回值) 使用场景: 使用多线程任务校验被保人数据; 1、每个被保人都是一个线程任务, 2、每个线程任务执行完都需要告诉主线程执行成功还是失败 3、这里需要submit提交Callable任务返回Future对象,并通过Future.get方法来获取执行结果 本地代码示例: 1.传统spring项目: 调用接口: public class HmoPushOrderServiceImpl implements HmoPushOrderService { @Override public ResponseDTO<Boolean> pushHmoUserToPingAn() { ResponseDTO<Boolean> res = new ResponseDTO<Boolean>(); ExecutorService threadPool = PingAnUserActivateTaskThreadPool.getInstance().getThreadPool(); threadPool.submit(new PingAnActivateThreadTask()); res.setResult(ResponseCode.SUCCESS, "触发成功", Boolean.TRUE); return res; } 异步线程池创建类: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class PingAnUserActivateTaskThreadPool { // 最大线程数 private static final int MAX_QUEUE_SIZE = 5; private static ExecutorService exc = null; private static volatile PingAnUserActivateTaskThreadPool instance = null; private PingAnUserActivateTaskThreadPool() { exc = Executors.newFixedThreadPool(MAX_QUEUE_SIZE); } /** * * @return */ public static PingAnUserActivateTaskThreadPool getInstance() { if (instance == null) { synchronized (PingAnUserActivateTaskThreadPool.class) { if (instance == null) { instance = new PingAnUserActivateTaskThreadPool(); } } } return instance; } public ExecutorService getThreadPool() { return exc; } } 任务执行类: public class PingAnActivateThreadTask implements Runnable { public PingAnActivateThreadTask() { hmoPushOrderManager = SpringContextUtil.getBean("hmoPushOrderManager"); msgSender = SpringContextUtil.getBean("msgSender"); configUtils = SpringContextUtil.getBean("configUtils"); apiUserBindManager = SpringContextUtil.getBean("apiUserBindManager"); } @Override public void run() { ... } } 2.spring-boot项目: 业务Controller: @RestController @RequestMapping("hfxyk/") public class HfxykDataController { @Resource private ExecutorService taskExecutor; taskExecutor.submit(new Runnable() { @Override public void run() { ... } } } 线程池配置公用类: import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @Configuration public class ThreadPoolConfig { /** * 公共业务线程池 * * @return */ @Bean(name = "taskExecutor") public ExecutorService executorService() { ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("executor-hfxyk-pool-%d").build(); return new ThreadPoolExecutor(2, 8, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), namedThreadFactory, new ThreadPoolExecutor.DiscardPolicy()); } } 注意:DiscardPolicy策略:对拒绝任务直接无声抛弃,没有异常信息。 你可以使用JDK自带的监控工具来监控我们创建的线程数量,运行一个不终止的线程,创建指定量的线程,来观察: 工具目录:C:\Program Files\Java\jdk1.6.0_06\bin\jconsole.exe 运行程序做稍微修改: Java代码 package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService singleThreadExecutor = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { public void run() { try { while(true) { System.out.println(index); Thread.sleep(10 * 1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/105754061
-
Http目前协议版本是1.1,HTTP是一种无状态的协议,无状态是指Web浏览器与Web服务器之间,不需要建立持久的链接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(response),链接就被关闭了,在服务器端不保留链接的有关信息。 1.建立TCP链接 在HTTP工作前,首先浏览器与服务器建立链接,该链接是通过TCP完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议方案。一般TCP连接端口号是80。 打开浏览器,地址栏输入blog.csdn.net,开始进行域名解解析: (1).浏览器自身搜dns缓存 (2).搜索操作系统的dns缓存 (3).读取本地host文件 浏览器发起一个dns系统调用,浏览器获得域名对应的ip地址后,发起一个http三次握手 tcp/ip链接建立起来后,服务器端接受到请求,根据路径参数,经过后端的处理后,把处理后的结果返回给浏览器,浏览器拿到html页面代码,解析和渲染页面,里面的js、css图片资源都需要经过上面的步骤。 2.Web浏览器向服务器发送请求命令 例如:GRT/sample/hello.jsp HTTP/1.1 3.Web浏览器发送请求头信息 浏览器发送请求命令后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送一空白行通知服务器,它已经结束了该头信息的发送。 4.Web服务器应答 应答的第一部分是协议的版本号和应答状态码 如:HTTP/1.1 200OK 5.服务器发送应答头信息(关于服务器自己的数据) 6.服务器向浏览器发送数据 服务器发送头信息后,它会发送一个空白行来表示头信息发送到此为止,接着以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。 7.关闭TCP连接 如果浏览器或服务器在其头信息加入了这行代码,Connection:keep-alive,TCP连接在发送后仍然保持打开状态 HTTP请求信息由3部分组成 1.请求方法URL 协议/版本 GET/sample.jsp HTTP/1.1 2.请求头(包含客户端环境、浏览器所用语言、正文长度等) 3.请求正文(包含客户提交的查询字符串信息) userName=qwe&passWord=123 HTTP响应3个部分构成 1.协议状态版本 HTTP/1.1 200OK 2.响应头(服务器类型、日期时间、内容类型、长度等) Server:Apache Tomcat/5.0.12 Content-Type:text/html 3.响应正文 <html> <head> <title>HTTP响应示例</title> </head> </html>
-
哪些协议是无状态协议哪些是有状态的,怎么区别? 举个例子我和朋友出去吃饭 不需要每次报上姓名 联系方式 等 朋友就知道我是谁 这是有状态的 而我去办事大厅 工作人员不会记得我是谁 每次去都要填表 出示身份证 这就是无状态的 无状态协议:在下一次链接不记住这一次链接的信息。 HTTP,UDP都是无状态协议 TCP,FTP是有状态协议 无状态服务器是指一种把每个请求作为与之前任何请求都无关的独立的事务的服务器 <<tcp/ip 协议族>>(第二版)第546页有这样一句话: 虽然HTTP使用TCP的服务,但HTTP本身是无状态协议.客户发送请求报文来初始化这个事务.服务器发送响应来回答. 暗示了TCP协议是一个有状态的协议 http协议与tcp、udp区别 http:是用于www浏览的一个协议。 tcp:是机器之间建立连接用的到的一个协议。 TCP协议与UDP协议是传输层协议。 HTTP协议是应用层协议。 Dubbo支持的协议 在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情。你可以根据你应用的创建来选择。例如,使用RMI协议,一般会受到防火墙的限制,所以对于外部与内部进行通信的场景,就不要使用RMI协议,而是基于HTTP协议或者Hessian协议。Dubbo支持8种左右的协议,如下所示: (1) dubbo:// Dubbo协议 (2) rmi:// RMI协议 (3) hessian:// Hessian协议 (4) http:// HTTP协议 (5) webservice:// WebService协议 (6) thrift:// Thrift协议 (7) memcached:// Memcached协议 (8)redis:// Redis协议 在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情。你可以根据你应用的创建来选择。 例如,使用RMI协议,一般会受到防火墙的限制,所以对于外部与内部进行通信的场景,就不要使用RMI协议,而是基于HTTP协议或者Hessian协议。 部分协议的特点和使用场景如下: 1、dubbo协议 Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。 连接个数:单连接 连接方式:长连接 传输协议:TCP 传输方式:NIO异步传输 序列化:Hessian二进制序列化 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。 适用场景:常规远程服务方法调用 为什么要消费者比提供者个数多: 因dubbo协议采用单一长连接, 假设网络为千兆网卡(1024Mbit=128MByte), 根据测试经验数据每条连接最多只能压满7MByte(不同的环境可能不一样,供参考), 理论上1个服务提供者需要20个服务消费者才能压满网卡。 为什么不能传大包: 因dubbo协议采用单一长连接, 如果每次请求的数据包大小为500KByte,假设网络为千兆网卡(1024Mbit=128MByte),每条连接最大7MByte(不同的环境可能不一样,供参考), 单个服务提供者的TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。 单个消费者调用单个服务提供者的TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。 如果能接受,可以考虑使用,否则网络将成为瓶颈。 为什么采用异步单一长连接: 因为服务的现状大都是服务提供者少,通常只有几台机器, 而服务的消费者多,可能整个网站都在访问该服务, 比如Morgan的提供者只有6台提供者,却有上百台消费者,每天有1.5亿次调用, 如果采用常规的hessian服务,服务提供者很容易就被压跨, 通过单一连接,保证单一消费者不会压死提供者, 长连接,减少连接握手验证等, 并使用异步IO,复用线程池,防止C10K问题。 2、RMI RMI协议采用JDK标准的java.rmi.*实现,采用阻塞式短连接和JDK标准序列化方式 Java标准的远程调用协议。 连接个数:多连接 连接方式:短连接 传输协议:TCP 传输方式:同步传输 序列化:Java标准二进制序列化 适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。 适用场景:常规远程服务方法调用,与原生RMI服务互操作 3、hessian Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现 基于Hessian的远程调用协议。 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:Hessian二进制序列化 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。 适用场景:页面传输,文件传输,或与原生hessian服务互操作 4、http 采用Spring的HttpInvoker实现 基于http表单的远程调用协议。 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:表单序列化(JSON) 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。 适用场景:需同时给应用程序和浏览器JS使用的服务。 5、webservice 基于CXF的frontend-simple和transports-http实现 基于WebService的远程调用协议。 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:SOAP文本序列化 适用场景:系统集成,跨语言调用。 6、thrif Thrift是Facebook捐给Apache的一个RPC框架,当前 dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如service name,magic number等。 ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/107820609
-
在java里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝。 浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化。 深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。 方式1:构造函数深拷贝 package com.lyj.demo.pojo.cloneTest; import lombok.Getter; /** * @author 凌兮 * @date 2021/4/15 14:28 * 通过构造器进行深拷贝测试 */ @Getter public class UserConstruct { private String userName; private AddressConstruct address; public UserConstruct() { } public UserConstruct(String userName, AddressConstruct address) { this.userName = userName; this.address = address; } public static void main(String[] args) { AddressConstruct address = new AddressConstruct("小区1", "小区2"); UserConstruct user = new UserConstruct("小李", address); // 调用构造函数进行深拷贝 UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2())); // 修改源对象的值 user.getAddress().setAddress1("小区3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1()); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); // true System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2())); } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /** * @author 凌兮 * @date 2021/4/15 14:28 */ @Getter @Setter public class AddressConstruct { private String address1; private String address2; public AddressConstruct() { } public AddressConstruct(String address1, String address2) { this.address1 = address1; this.address2 = address2; } } 方式2:重载Clone()方法深拷贝 Object父类有个clone()的拷贝方法,不过它是protected类型的 ,我们需要重写它并修改为public类型,除此之外,子类还需要实现Cloneable接口来告诉JVM这个类上是可以拷贝的。 package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /** * @author 凌兮 * @date 2021/4/15 14:49 * */ @Setter @Getter public class AddressClone implements Cloneable{ private String address1; private String address2; public AddressClone() { } public AddressClone(String address1, String address2) { this.address1 = address1; this.address2 = address2; } @Override protected AddressClone clone() throws CloneNotSupportedException { return (AddressClone) super.clone(); } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /** * @author 凌兮 * @date 2021/4/15 14:48 * 通过实现Clone接口实现深拷贝 */ @Setter @Getter public class UserClone implements Cloneable{ private String userName; private AddressClone address; public UserClone() { } public UserClone(String userName, AddressClone address) { this.userName = userName; this.address = address; } /** * Object父类有个clone()的拷贝方法,不过它是protected类型的, * 我们需要重写它并修改为public类型。除此之外, * 子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。 * @return * @throws CloneNotSupportedException */ @Override protected UserClone clone() throws CloneNotSupportedException { // 需要注意的是,super.clone()其实是浅拷贝, // 所以在重写UserClone类的clone()方法时,address对象需要调用address.clone()重新赋值 UserClone userClone = (UserClone) super.clone(); userClone.setAddress(this.address.clone()); return userClone; } public static void main(String[] args) throws CloneNotSupportedException { AddressClone address = new AddressClone("小区1", "小区2"); UserClone user = new UserClone("小李", address); UserClone copyUser = user.clone(); user.getAddress().setAddress1("小区3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } } 需要注意的是,super.clone()其实是浅拷贝,所以在重写User类的clone()方法时,address对象需要调用address.clone()重新赋值。 方式3:Apache Commons Lang序列化方式深拷贝 Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。 Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。 package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; import java.io.Serializable; /** * @author 凌兮 * @date 2021/4/15 15:11 */ @Getter @Setter public class AddressSerializable implements Serializable { private String address1; private String address2; public AddressSerializable() { } public AddressSerializable(String address1, String address2) { this.address1 = address1; this.address2 = address2; } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.SerializationUtils; import java.io.Serializable; /** * @author 凌兮 * @date 2021/4/15 15:10 * 通过Apache Commons Lang 序列化方式深拷贝 * Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。 * 但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。 * Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。 */ @Getter @Setter public class UserSerializable implements Serializable { private String userName; private AddressSerializable address; public UserSerializable() { } public UserSerializable(String userName, AddressSerializable address) { this.userName = userName; this.address = address; } public static void main(String[] args) { AddressSerializable address = new AddressSerializable("小区1", "小区2"); UserSerializable user = new UserSerializable("小李", address); UserSerializable copyUser = SerializationUtils.clone(user); user.getAddress().setAddress1("小区3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } } ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/130907780
-
一、定义 双冒号运算操作符是类方法的句柄,lambda表达式的一种简写 表达式: person -> person.getName(); 可以替换成: Person::getName 表达式: () -> new HashMap<>(); 可以替换成: HashMap::new 二、如何理解 双冒号(::)运算符在Java 8中被用作方法引用(method reference),方法引用是与lambda表达式相关的一个重要特性。它提供了一种执行方法的方法,为此,方法引用需要由兼容的函数式接口组成的目标类型上下文。使用lambda表达式会创建匿名函数, 但有时候需要使用一个lambda表达式只调用一个已经存在的方法(不做其它), 所以这才有了方法引用! 其实,JVM 本身并不支持指向方法引用,过去不支持,现在也不支持。Java 8 对方法引用的支持只是编译器层面的支持,虚拟机执行引擎并不了解方法引用。编译器遇到方法引用的时候,会像上面那样自动推断出开发者的意图,将方法引用还原成接口实现对象,或者更形象地说,就是把方法引用设法包装成一个接口实现对象,这样虚拟机就可以无差别地执行字节码文件而不需要管什么是方法引用了。 需要注意的是,方法引用是用来简化接口实现代码的,并且凡是能够用方法引用来简化的接口,都有这样的特征:有且只有一个待实现的方法。这种接口在 Java 中有个专门的名称: 函数式接口。当试图用方法引用替代一个非函数式接口时,会有这样的错误提示: xxx is not a functional interface。 三、使用场景 类型 引用语法 案例 引用静态方法 类名::静态方法名 Integer::parseInt 引用特定对象实例方法 对象::实例方法名 System.out::println 引用特定类型的任意对象的实例方法 特定类型::实例方法名 String::compareToIgnoreCase 引用超类(父类)实例方法 super::方法名 引用类构造方法 类名::new ArrayList::new 引用数组构造方法 数组类型[]::new String[]::new 案例详解: 引用静态方法 import org.junit.Test; import java.util.Arrays; import java.util.List; public class Colon{ @Test public void test(){ List<String> list = Arrays.asList("a", "b","c"); //静态方法引用ClassName::methodName list.forEach(Colon:: print); //上一行等价于 //1ist.forEach((x)->Colon.print(x)); } //静态方法 public static void print(String s){ system.out.println(s); } } 引用特定对象实例方法 import org.junit.Test; import java.util.Arrays; import java.util.List; public class Colon{ @Test public void test(){ List<string> list = Arrays.asList("a", "b","c");//r实例方法引用instanceRef: :methodName list.forEach(new Colon()::print); //上一行等价于 //iist.forEach((x)->new Colon().print(x)); } //实例方法 public void print(String s){ System.out.println(s); } } 引用特定类型的任意对象的实例方法 import org.junit.Test; import java.util.Arrays; public class Colon{ @Test public void test(){ String[] arr = { "Barbara","James","Mary", "John", "Patricia","Robert","Michae1", "Linda”}; //引用String类型的任意对象的compareToIgnoreCase方法实现忽略大小写排序 Arrays.sort(arr, String::compareToIgnoreCase); //上一行等价于 //Arrays.sort(arr, (a,b)->a.compareToIgnoreCase(b)); //输出 for(String s:arr){ System.out.println(s); } } 引用超类(父类)实例方法 import org.junit.Test; import java.util.Arrays; import java.util.List; public class Colon extends BaseColon{ @Test public void test(){ List<string> list = Arrays.asList("a", "b","c"); //实例方法引用instanceRef::methodName list.forEach(super:: print); } } class Basecolon{ //实例方法 public void print(string s){ System.out.println(s); } } 引用类构造方法 一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。 //注意:该类无需实现接口 public class Colon{ private String name; private int age; //无参构造 public Colon(){ } //有参构造 public colon(String name, int age){ this.name = name; this.age = age; } public static void main(String[] args){ //无参构造引用 ColonNoParam cnp = Colon::new; colon c1 = cnp.createColon(); System.out.println(c1); //有参构造引用 ColonlithParam cwp = Colon::new; colon c2 = cwp.createColon("小明",20); System.out.println(c2); } //生成toString方法打印查看 @Override public string toString() { return "Colon{" + "name='"+ name + "\"+ ",age=" + age + '}'; } } interface colonNoParam{ //无参方法提供对象 Colon createColon(); } interface ColonwithParam{ //有参方法提供对象(数据类型要与colon有参构造函数对应) colon createColon(String s,int i); } 引用数组构造方法 我们可以借助jdk自带的java.util.function.Function类实现对数组构造函数的引用。 当然,我们也可以使用@FunctionalInterface自定义函数式接口来实现: public class Colon{ public static void main(string[] args) { MyArrayFunction<Integer,Colon[]> function = Colon[]::new; //调用apply方法创建数组,这里的5是教组的长度 colon[] arr = function.apply(5); //循环输出-初始都为null for(Colon c:arr){ System.out.println(c); } } } //自定义函教式接口 @FunctionalInterface interface MyArrayFunction<A,T>{ T apply(A a); } 参考博客:https://blog.csdn.net/yangzhe19931117/article/details/128246653 ———————————————— 版权声明:本文为CSDN博主「橙橙爱学习」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_52293201/article/details/129433338
-
🍒java中二维数组的定义和赋值 int[][] array = new int[3][]; //java行不能省略,列可以省略 array[0] = new int[]{0, 1, 2};//赋值 array[2] = new int[]{4, 5, 6}; int[][] array1={{1,1},{2,2},{3,3}};//定义并且初始化 🍒二维数组遍历的三种方法 二维数组其实就是特殊的一维数组; 在java中将这句话诠释得淋漓尽致; 🍇第一种:for循环遍历 int[][] array = new int[3][]; //java行不能省略,列可以省略 array[0] = new int[]{0, 1, 2}; array[2] = new int[]{4, 5, 6,7}; for(int i=0;i< array.length;i++){ if(array[i]==null) { System.out.println("null"+" "); continue; }//当二维数组某一行为空时直接跳过循环遍历下一行 for (int j = 0; j < array[i].length; j++) { System.out.print(array[i][j]+" "); }//二维数组的每一行元素都相当于一个一维数组, //遍历一维数组,长度就是array[i].length System.out.println(); } 运行截图: 🍇第二种方法:通过Arrays.deepToString()遍历 int[][] array = new int[3][]; //java行不能省略,列可以省略 array[0] = new int[]{0, 1, 2}; array[2] = new int[]{4, 5, 6,7}; System.out.println(Arrays.deepToString(array)); 运行截图: 🍇第三种方法:通过for( : )遍历 int[][] array = new int[3][]; //java行不能省略,列可以省略 array[0] = new int[]{0, 1, 2}; array[2] = new int[]{4, 5, 6,7}; int i=0; for(int[] ret:array){ if(array[i]==null) { System.out.println("null"+" "); i++; continue; } for(int x:ret){ System.out.print(x+" "); } System.out.println(); i++; 注意:冒号左边填写的是数组每个元数的类型,右边填写的是数组名 ———————————————— 版权声明:本文为CSDN博主「熬夜退役选手337」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_61638178/article/details/131697975
-
在 Java 中,类加载的流程有一个专门的机制叫做“类加载机制”。类加载机制是指一个类在 Java 虚拟机(JVM)中的执行流程,它也是 Java 程序能够正常执行的关键所在,那它的具体执行流程是啥?接下来我们一起来看。流程概述在 JVM 中,类加载会经历以下 5 个阶段:加载阶段(Loading)验证阶段(Verification)准备阶段(Preparation)解析阶段(Resolution)初始化阶段(Initialization)其中:验证阶段、准备阶段和解析阶段合起来又称为连接阶段,所以以上 5 个阶段又可以划分为 3 大类:加载阶段(Loading)连接阶段(Linking)验证阶段(Verification)准备阶段(Preparation)解析阶段(Resolution)初始化阶段(Initialization)这 3 大类、5 个流程的具体执行细节是这样的。1.加载阶段简单来说,加载阶段就是将类文件加载到内存中的过程。在加载阶段,JVM 需要完成以下 3 件事:通过一个类的全限定名来获取定义此类的二进制字节流;将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。2.连接阶段连接阶段又分为:验证阶段(Verification)、准备阶段(Preparation)和解析阶段(Resolution),具体执行的细节如下。2.1 验证阶段验证阶段也叫做校验阶段,它主要是用来验证加载到内存中的类是否是安全合规的文件,验证的主要动作大概有以下几个(当然,以下细节如果实在记不住也没关系):文件格式校验包括常量池中的常量类型、Class 文件的各个部分是否被删除或被追加了其他信息等;元数据校验包括父类正确性校验(检查父类是否有被 final 修饰)、抽象类校验等;字节码校验,此步骤最为关键和复杂,主要用于校验程序中的语义是否合法且符合逻辑;符号引用校验,对类自身以外比如常量池中的各种符号引用的信息进行匹配性校验。2.2 准备阶段准备阶段就开始给类中的静态变量设置默认值了,注意这里不是给静态变量设置初始值,而是设置默认值,二者还是有很大区别的。举个例子,比如代码中写的内容是:public static int number = 10;那么此时是给 number 变量设置的 int 值是默认值 0,而非初始值 10。2.3 解析阶段解析阶段就是将常量池中的符号引用更换成直接引用了,所谓的符号引用是指以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可;而直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。符号引用和直接引用有一个重要的区别:使用符号引用时被引用的目标不一定已经加载到内存中;而使用直接引用时,引用的目标必定已经存在虚拟机的内存中了。3.初始化阶段初始化阶段,Java 虚拟机真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。到这一步骤之后,类的加载过程就算正式完成了,此时会给静态变量设置初始值,并执行静态代码块的内容。总结类加载流程总共分为 3 大类,5 个主要流程:加载阶段(Loading):将类文件加载到内存。连接阶段(Linking)验证阶段(Verification):类文件安全性效验。准备阶段(Preparation):给静态变量设置默认值。解析阶段(Resolution):将符号引用转换为直接引用。初始化阶段(Initialization):执行静态代码块和给静态变量设置初始值。转载自https://www.cnblogs.com/vipstone/p/17069838.html
上滑加载中
推荐直播
-
华为云码道-玩转OpenClaw,在线养虾2026/03/11 周三 19:00-21:00
刘昱,华为云高级工程师/谈心,华为云技术专家/李海仑,上海圭卓智能科技有限公司CEO
OpenClaw 火爆开发者圈,华为云码道最新推出 Skill ——开发者只需输入一句口令,即可部署一个功能完整的「小龙虾」智能体。直播带你玩转华为云码道,玩转OpenClaw
回顾中 -
华为云码道-AI时代应用开发利器2026/03/18 周三 19:00-20:00
童得力,华为云开发者生态运营总监/姚圣伟,华为云HCDE开发者专家
本次直播由华为专家带你实战应用开发,看华为云码道(CodeArts)代码智能体如何在AI时代让你的创意应用快速落地。更有华为云HCDE开发者专家带你用码道玩转JiuwenClaw,让小艺成为你的AI助理。
回顾中 -
Skill 构建 × 智能创作:基于华为云码道的 AI 内容生产提效方案2026/03/25 周三 19:00-20:00
余伟,华为云软件研发工程师/万邵业(万少),华为云HCDE开发者专家
本次直播带来两大实战:华为云码道 Skill-Creator 手把手搭建专属知识库 Skill;如何用码道提效 OpenClaw 小说文本,打造从大纲到成稿的 AI 原创小说全链路。技术干货 + OPC创作思路,一次讲透!
回顾中
热门标签