Java拓展-拆,装箱,线程,反射

2023-08-10,

导言:

在学习JavaSE的时候,我们会使用Java基础编程,并且了解了什么是面向对象的编程,会使用Java写一些基础算法程序,

接下来,我们需要了解Java的自动拆箱和自动装箱,单线程和多线程,反射是什么,值得注意的是,讲的是Java中的特性,但是OOP语言其实都是用这些操作的,只是小部分不同罢了

一.自动拆箱和自动装箱

不管是拆箱还是装箱,它的宏观意义就是普通类型到封装类型(对象)的转换,反之,就是封装类型到基本类型的转换

自动装箱

自动装箱指的是普通类型到封装类型(对象)的转换

我们可以看到上面的代码:

Integer b = new  Integer(10);这是很标准的创建对象的方式

我们在使用  Integer  a  =  10  ;的时候,其实就已经发生了一次自动装箱,我们可以拆分一下这句话,Integer 是封装类型,也就是说一般是通过new的形式拿到的,

但是后面的 a  =  10; 一般都是定义一个int型变量才会那么定义,你就会发现这句话好像有两个定义,一个像是在定义基本类型,另一个像是在定义封装类型,但是这样的定义是可以完成的,且不会报错

这就要基于Java的隐式调用:

如图,我们的定义在JVM处理的时候自动隐式的调用了valueOf()方法,由于是隐式的,在我们不知到的情况下完成的就叫它自动装箱

通过上面的程序,我们比较a和b的输出结果,发现是Ture,说明都是Integer类型,自动装配就等同new 出来的Integer对象

自动拆箱

自动拆箱指的是封装类型(对象)到普通类型的转换

根据上面的图片我们可以看到:

int a = 1;是很明显的int型的定义方式

Integer integer = new Integer(1);  我们是new了一个Integer的对象类型,所以integer现在是一个对象类型,他要是想和基本类型比较肯定自己也是int
但是我们使用的是  int b = integer; 让一个int 类型直接 被赋值于 integer这个对象类型,乍一眼看,类型不匹配啊,但结果也是可执行的,且不会报错
这也要归功于Java的隐式调用:

它的处理也是隐式调用integer对象转为int型变量的方法,valueOf()方法,由于是隐式完成的,所以叫自动拆箱

我们再来看看上面的程序运行结果,a==b结果是Ture,所以它们最后的结果都是int型

拓展自动装箱:

自动装箱的时候在代码中我们看到了,比较a和b两个对象的时候,我是使用的equals方法,它比较的是值,也就是两个对象的值都是10,所以equals方法的结果是ture

但是我们要是通过 == 来判定的时候呢?结果是 false 这是为什么呢?

首先,Integer b = new Integer(10); 它申请的空间肯定是在堆上的,因为是程序运行时动态开辟的空间,主要是Integer a = 10;它的空间在那?

我们这个时候就需要看一波自动装箱的源码了:

这段源码的意思是,我们在使用valueOf()方法的时候,不会直接创建一个对象,而是先看看要创建的对象有没有被先加载好,因为Java在缓存中已经预加载了256个Integer对象,范围是-128~127,如果在此范围内,那么就直接拿缓存中的对象,

如果不在此范围内,才会去构建一个新的对象,这是因为大多数情况,我们在使用自动装箱和自动拆箱的时候,所转换的数据大多是用来做状态码的,它的大小不会超过三位,所以我们先预先构造一些,就避免了反复造轮子

现在我们看看都是用vlaueOf()构造出来的对象是不是相等的:

当我们把代码改成上面的方式构造对象后,a对象和b对象就是相等的了,即结果为Ture

接下来我们可以思考一下:

Integer  a = 128;

Integer  b = 128;

输出:a == b ,结果如何呢?

包装类型缓存池

在jvm中,缓存中会预先加载一些封装对象,目的是为了提高封装对象的重复利用率,以免我们在使用这些封装对象的是时候重复构造

如图,在jvm会预先加载这八种封装类型,也就是每个基本类型对应的封装类型:

它们预加载范围如图所示

所以,我们的程序在运行的时候,一旦发生需要装箱和拆箱的操作的时候,尽量不要使用new  直接创建,而是通过valueOf()方法,先去缓存区拿,如果拿不到,再去创建一个新的对象,

这就是我们使用valueOf()方法区去创建对象的过程,比直接new 封装对象要多了一步去查看缓存的操作

二.单线程和多线程

线程这一概念,我们在了解的时候一定是在操作系统中,随着技术的发展,我们使用的单线程编程,效率执行低,所以现在多线程编程又变的流行起来了

线程是操作系统的最小调度单位,它被包含到进程之中,是进程运行中的最小运行单位

进程是程序运行的基本实体

在操作系统中,我们了解到了进程是程序运行资源的拥有者,而线程则是负责调度可资源分配者,本身不需要分配资源

单线程:

单线程最大的特点就是程序的顺序的执行,它的编程风格通俗易懂,它只有在程序执行完成了以后才会执行后面的程序,单线程较多线程来说,系统稳定、扩展性极强、软件丰富。多用于点对点的服务。

单线程处理的优点:同步应用程序的开发比较容易,但由于需要在上一个任务完成后才能开始新的任务,所以其效率通常比多线程应用程序低。如果完成同步任务所用的时间比预计时间长,应用程序可能会不响应。多线程处理可以同时运行多个过程。

我们也可以看上面的实例,当a=1被处理器处理完成以后,等到b=2会有一段时间才会到达处理器,处理器的速度是很快的,从内存调过来的时间,处理器就一直处于等待的状态,所以这段时间造成了处理器的浪费,

为了节约处理器的资源,我们便研究出来了多线程

多线程:

简单引入:在程序中独立,但是可以同时运行

我们先简单的看个小实例预热一下多线程:

我们可以看到上面的图片,我们的传送带每5分钟只会来一个货物,而我们的工人呢?只需要20s就可以把货物卸载完成了,很显然,这个工人有很大的一部分时间都在摸鱼

至少有4分钟它一直在等待传送带运来货物,我们要是这么设计工作者岗位的话,肯定很浪费,有什么办法可以把它调高呢?

我们可以使用多增加几个传送带:

我们在增加了传送带后,虽然还是有空闲时间,但是利用率变高了,除去员工跑来跑去的开销,以及工作的开销,我们对整个线路的提高都是有目共睹的,

现在我们的员工执行逻辑就不再是第一次那么简单的卸载,然后一直等待了,而是需要利用等待的时间去处理下一条传输带,

这就是多线程,它旨在相同的前提下,更多的处理任务

现在我们的生活中无处不存在多线程,你有没有想过为什么在打游戏的时候,我们的手机还能听歌,在追剧的时候还能聊天,要是依旧使用单线程,肯定一个终端只能运行一个设备

多线程提高了效率,可以在同一段时间做更多的事情

多线程之并发:

在同一时刻有多个指令在CPU上交替执行

多线程之并行:

在同一时刻有多个指令在CPU上同时进行

下面我们来看看多线程在程序中是怎么执行的:

我们可以看到,理想状态下,我们的公共资源就是6,所以两个线程都可以拿到6,然后执行加一操作后输出并写回公共资源,这种执行方式肯定是多线程无错误的

但是,线程虽然可以同时间段运行,由于各种原因的混合,导致了一个先读入数据,一个后读入数据,中间的一点点空隙就会导致结果不一样,

比如第二种情况,就是在线程1已经写回了情况,线程2才去读临界资源,导致了最后的结果两者对不上,

所以我们看到了多线程带来的高效,即同时处理两个问题,但是它也带来了很多问题,就是多个线程读取数据结果不一致

当然,多线程发展到今天,我们也有很多方法解决它,解决也是要开销的

三.反射机制

在了解反射之前,我们一定要知道反射的作用是干什么的,众所周知,Java是一门静态语言,但是在Java引入了反射机制以后了它便可以编程一门准动态语言

什么是动态语言呢?

1、动态语言

是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。

主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。

2、静态语言

与动态语言相对应的,运行时结构不可变的语言就是静态语言。如 Java、C、C++。

正常的方式编程:

引入需要使用的包类名   ----   通过new关键字实例化   ---    取得实例化对象

反射的方式编程:

实例化的对象   ---  getClass()方法   ----    得到完整的包类名

反射就好比一面镜子,可以通过镜子找到我们所需要的方法,对象

反射相关的API:

java.lang.Class                           //代表一个类

java.long.reflect.method            //代表一个方法

java.long.reflect.Field                //代表一个成员变量

java.long.reflect.Constuctor      //代表一个构造器

反射的实现方式:

Class c = Class.forName("java.long.String");

java.long.String是包名,也就是包的位置

获得的就是一个class对象

通过包名拿到的类其实即使拿了两次,发现它们的hashCode()其实是相等的

这也和Java的内存模型有关,相同的类只会加载一次

通过getClass方法可以拿到任何类,原因是此方法是Object对象的,所以它默认被所有的类继承

为什么反射能拿到类也要归功于jvm中相同的类只有一个

正常解析的方式是我们在编写程序的时候,通过类名,然后使用new关键字直接创建一个对象

我们要知道,不管你有几个对象,它们都能找到是通过类名给new出来的,不管有多少个类,实际上它们都是归于了Class类

所以反射可以通过对象拿到类,即使它不知道整个类是什么,但是一定在Class类里面

这就是我们的返回值是 Class<?> 的原因

反射小结:

    通过反射,我们可以是Java完成部分动态语言的功能
    通过反射,可以使用到特殊的方法,比如私有方法,属性
    很多框架都是反射机制来实现的,比如Spring  AOP的思想
    当某个功能可以不使用反射时,尽量不适用(反射麻烦,效率低)

-------------------

Java反射和多线程编程都是可实现的,以上的博客内容都是概述,真正的实现还需要我们专门花时间去学习,想通过一般的博客去搞懂反射和多线程编程,显得有些天方夜谭了

------------------

Java拓展-拆,装箱,线程,反射的相关教程结束。

《Java拓展-拆,装箱,线程,反射.doc》

下载本文的Word格式文档,以方便收藏与打印。