摘要:为何学习Java及Java的基础语法知识,记事本的显示原理,Java中的重要知识点
Java学习笔记:2022年1月2日1.正式开始学习Java!1.参考用书以及注意事项2.为何学习Java3.Java基本术语4.Java基础知识1.Java中的命名问题2.作用域3.Java中的类型4.反码与补码5.精度问题5.计算机中的显示原理6.Java中的重要知识点1.final标识符2.重要的两个数学方法pow和sqrt3.Java中的类型转换4.Unicode中的码点和代码单元5.布尔类型为何占用32比特
我这次对于Java的学习使用到的参考书是《Java核心技术 卷一》,《Java核心技术》是一套翻译书,原著是美国的霍斯特曼,属于Java开发的核心角色,对于这本书基本上可以当权威著作来参考,其出版社为Pearson出版社,这个出版社是一个全世界范围内都很著名的出版社,因此基本上可以对内容放心,当然对于翻译者我也放心。在互联网面试时,基本上90%的面试题出自于这本书的2,3,4,5章,这个需要额外注意。
不知道大家有什么更高尚的理由,我就是为了就业,或者说实现人生价值?话虽如此,实际上我们学习Java的原因大部分出于社会需求,也就是说当前的网络市场上还是需要Java开发工程师的,说人话就是学Java挣钱的概率稍微高一点。为什么会用这种情况?我认为,这其实是Java应用面比较广的体现,实际上,大部分的互联网公司在干的事情就是鼓捣各种新的营销思路,然后用网页或者说软件的方式体现出来,尽管只是一个体现方式,但在需求者的敦促下,其展现方式也是发展繁茂,各种需求与日俱增。Java是用来做网页的最多的语言,因为Java有着良好的运行速度,其次Java支持分布式,什么是分布式?分布式就是在做大型项目时,可以多台服务器同时支持一个项目,将一个项目分散到多个服务器中。这样一来,整个项目的健壮性、可扩展性以及最基础的性能就得以大幅度提高。同时Java还是一种面向对象的编程语言,这中面向对象的思想在当今的开发中非常方便,是一种先进的编程思想。然而不得不提的是当今大部分编程语言都具备面向对象的思想,Java这种面向对象特性只不过是相对于早期的如C/C++这种面向过程的编程语言罢了。同时还值得明白的一点是:“Java语言没有任何优越性,任何可以用Java实现的功能用其他语言都能实现,只不过工作量以及运行效率不同。”我们使用C语言也可以实现Java语言实现的所用功能,只不过可能需要比Java更多的代码行数罢了。任何语言都有自己擅长的领域,我们通常说某项语言擅长解决什么方面的问题,实际上这种语言的现有的库中已经写好了很多关于这方面的操作,我们不用自己从0开始实现罢了。
如今Java开发仍然是比较火热(大概吧),首先网页基本上都是用JavaWeb实现的,手机上的应用App也基本上是Java,实际上,安卓系统就是基于Java语言的,有些小游戏也是基于Java的,尽管不多。因此我要学习Java了!。
JDK,Java Development Kit的缩写,意思是Java开发套件,书上给出的解释是开发Java程序的程序员使用的软件。JDK实际上就是Java开发所要使用到的编程环境,它提供了Java编译调试以及运行等功能,为程序员提供了一个最基本的程序开发环境。
JRE,jre是用来运行Java程序的软件,它的功能是将class文件翻译成C语言乃至汇编语言然后提交给系统运行。通常情况下,JDK中是自带一个jre的,因为在程序开发中避免不了试运行。从翻译来看,JRE的意思是Java运行时环境,它通常属于 Java 开发套件(JDK)的一部分,有时也可以单独下载安装使用。JRE 由 Java 虚拟机(JVM)、Java 类库和 Java 类加载器构成。
JVM,简称Java虚拟机,Java虚拟机是用来运行class文件的,实际上Java虚拟机就是Java运行时的文件状态,二者本质上是一个东西,当把Java虚拟机跑起来,在内存中开辟的那一块空间就是Java运行时。
总体上,JDK 用于开发 Java 软件, JRE 用于提供编程工具和部署技术,而 JVM 则用于执行 Java 程序。这里仍然有些疑问,关于Java虚拟机和Java运行时的关系仍然不太明白,仍然有待研究。
infoQ,这是一个国内的很不错的社区。
Java SE,Java核心功能,也就是Java本身初始自带的,最原始的功能,Java的基本操作都在这里了,尽管这是Java的核心操作,然而其他的更加复杂的功能都是人们用它开发出来的。
Java EE,就是javaWeb的开发,也叫java的全家桶,网站以及其所有系统级别的开发。我们可以理解为:所有的关于当前市面上的网站,Java应用以及Java系统级别的开发统称Java EE。
Java Me,Java me就是安卓开发,安卓是基于Java的,在安卓上的所有应用开发统称Java me。苹果手机使用的是ios系统,它和安卓系统不是同一系统,它是用object-c语言开发的,苹果公司的x-code支持这种语言的开发。
SDK,指的是自己写的库的用例,包含注释,例子等,保证其他人看了之后就知道这个库怎么用了。通常来说,第三方在开发一个java调用库的时候会发布一个调用样例或说是说明,用来解释说明。SDK中不止有文档,还会附上代码,就是一个简单的小样例,让大家更生动的了解到怎么使用。如一个游戏开发库的sdk里边会包含一个开发文档,以及会提供一个用这个库写的真实样例,也就是一个小游戏,比如之前我学习过光子云的联机库,里边就包含一个可以联机的小游戏的代码。
注意:在开发时,我们通常使用版本比较旧的东西,比如Java17已经横空出世了,但大部分厂家在开发时还在用Java8,这是因为软件的更新换代快,并且其本质是一些高低电平,因此比硬件更容易出故障,老版本的开发包更加稳定,出问题后网上的解决方案也很多,因为很多问题大家之前都遇到过,新版本开发包中会存在很多新bug,网上很少见现成的资料,通常是要向社区反馈,而这个过程时间就可能很长,因此就可能会比较耽误事。
命名问题是Java中的最基础也是最重要的问题。标准的命名方法有助于提升代码的可读性,进而对代码的维护有着很重要的意义。格式规范,见名知意的好的命名方式可以让人一言就明白这个名字代表了什么,它是什么,进而帮助没有参与过这个代码编写的人读懂代码,当代码进行转手的时候,接手的人能够很好的对这个项目进行维护。同时,这里是一个面试点,关于如何命名,我们一定要认真记住。
大驼峰命名法:每个单词的首字母大写,就叫大驼峰命名法。如“HelloWorld”就是一种大驼峰命名法的名字。
小驼峰命名法:第一个单词的首字母小写,其余后边的单词首字母大写,叫小驼峰命名法。如“myFirstName”就是一种小驼峰命名法的名字。
在Java中,面对不同的单位或者说是命名对象,有着不同的命名法则,一下命名法则需要重点记住:
类名:一律使用大驼峰命名。
变量名、文件名、方法名:使用小驼峰命名。
文件夹名:所有字母一律小写。
常量名称:所有字母一律大写。
Java程序是书写在类中的程序,所有的依托全在于类,类中有方法,方法中用各种操作,因此作用域这个概念在Java中就显得尤为重要起来。在Java中,类的声明是包含一对大括号的,方法的声明也是包含一对大括号,而循环语句中同样包含一对大括号,乃至于在Java中还有一个概念叫做“块”、“语句块”。块这个东西,实际上也是一种作用域。作用域顾名思义,就是某个东西能产生作用的范围,实际上,类的作用域就是这个类中定义的变量有效果的范围,如Person类中的变量name,在这个类中可以随便使用,但是如果在Car类中,如果没有声明这个变量,直接打上一个name变量,在编译时就会报错,我们必须也声明一个name变量才行。但是,Car类中的变量和Person中的同名变量已经完全没有关系也没有冲突,这就是作用域的一种表现。
同时方法中变量的声明也体现出了作用域思想,不同函数中定义的同名函数互相没有影响,而方法内定义的变量拿在外边也无法使用。包括语句块也有这种效果。这种作用域的现象实际上和Java程序在内存上运行的机制息息相关。这个现象的出现实际上和Java运行时中的栈区以及程序运行机制有关,这一点在之后的笔记中有详细的解析的。
Java中有八个基本类型,在之前的笔记中也提到了,分别为:byte,short,int,long,float,double,char,boolean。之前在笔记中可能提到过基本类型有一个特点就是所有的同类型的变量在内存中占用的大小其实是一致的,举例就是int型的10和int型的1356的内存空间都占用了32位,这是为什么呢?
之所以会这样,是因为在内存中,基本类型的存储使用的是二进制进行表示,在内存中需要参与计算时,他们都被保存成了二进制数,且将不同类型,不同需要的数字进行了划分,用不同位数的二进制数进行表示。用不同数位的二进制表示不同类型数字的方法来源于进制转换以及排列组合。对于二进制来说,每位只有0或者1两个选择,因此一个1位二进制数有两种可能,即0或1,而2位二进制数选择就变多了,有四种可能,随着数位的增多,不同的二进制数就越来越多,我们便可以规定不同的二进制数列表示一个十进制数,进而将我们的数字记录在计算机中。同时,十进制数是完全可以表示成二进制数并参与运算的,因为进制抓换确实可行。所以,根据这两个原理,人们将有不同表示需求的数字分成了不同的类型,在用特定的需求是,使用特定的数据类型即可。当我们使用到一个比较小的整数时,使用byte类型就好了,short类型只有八位,这八位二进制数可以表示2的八次方个十进制数,首位当做符号位的话,可以表示-127到正的127,加上补码原理后,我们可以将1000000这个-0表示成-128,这样bytet实际上就表示了-128到127。这个范围上的数都可以用这八位二进制进行表示,因此,在一个特定范围内的十进制数字都可以用一个固定位数的二进制数进行表示,这就是基本类型中,相同类型的数据在内存中所占大小相同的与原因。我个人认为,导致人类这样进行设计的原因主要在于数学定义与计算机组成原理,这是一个相当深层的知识点,对于简单的应用来说意义不大,我们只需要记住:相同类型的基本类型在内存中所占大小一样即可。这一点我同样打算再日后进行更详尽的分析。
数据类型 | 内存大小(bit) |
---|---|
byte | 8 |
short | 16 |
int | 32 |
long | 64 |
float | 32 |
double | 64 |
char | 视编码情况而定 |
boolean | 32 |
同时这里也有一个范围对照表,展示了整形的大小以及范围,一个字节即是8bit:
对于这些类型,记住即可。
在计算机中,所有的基本变量确实是以二进制代码存储的,但是,他们实际上都是由补码存储的,补码的好处在于,十进制中的a - b可以表示成a + (-b),简而言之,用补码表示的数字,对于他们的运算只有相加,我们单纯的将二进制数的第一位作为符号位后,并不容易运算,同时仅使用普通的二进制数还会有一个问题,那就是1000000和0000000表示的都是0,但是二者不一样,二者之中必有一个浪费,因此出现了补码。如何获得补码呢?补码获得的方式是反码加一。对于反码和补码,普通的二进制码我们便称之为:原码。
如何根据原码获得反码呢?就是按位取反,具体操作如下:
-9--->1 0 0 0 1 0 0 1 原码 1 1 0 1 1 0 除了首位的符号位不变,其余位数按位取反,获得反码 1 1 0 1 1 1 反码加一,即得到补码 -0--->1 0 0 0 0 0 0 0 原码 1 1 1 1 1 1 反码 0 0 0 0 0 0 补码 与此同时,正数的补码和原码一样,如: 19--->0 0 0 1 0 0 1 1 原码 1 0 1 1 0 0 反码 0 1 0 0 1 1 补码,正数的补码不满足负数补码的获取模式,正数的补码直接是原码本身,无需计算,这一点需要注意
补码有效的解决了双零问题,同时使用补码,可以用正数补码加负数补码表示减法,这样计算机中只需有加法,只要有加法器,就可以既计算加法也计算减法,对效率提升有着深远的意义。因此,在计算机中,无论什么类型的数字,都是使用补码来表示的。计算机内的补码问题涉及进制问题,是一个很重要的知识点,我打算再以后进行单独的研究。
PS:由于补码的缘故,计算机中只存在二进制相加,这方便了运算。同时在计算机中的二进制乘法更为便捷,在计算机中,二进制数乘一个2相当于往左移动一位,除二相当于往右移动一位,如00001000移动为00010000,就相当于乘了一个2,通过这个原理,在计算机中的乘除法会变得特别简单,这一点在之后的笔记中会更加详细的解释说明。
在计算机中存在数据存储的精度问题,因为计算机本身是离散的存储,对于连续的数字实际上是存在精度差的,用计算机无法很精确的表示一个小数点后有很多位的小数,为此人们设计了不少结构用于进行小数的表示。那在计算机中如何表示一个小数呢,人们提出了IEEE标准。以下以float类型为例:
0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32位 1位 8位 23位 符号位 阶数部分 尾数
如上述所示,float的32个位中被分成了三部分,分别是符号位,阶数部分,数值部分,符号位大家都清楚,阶数位存的是使用科学计数法是的阶数,而数值部分则存取了多个数值。整个float类型的真实值为:float大小 = 尾数 * (2^(阶数部分 - 127))。之所以要减去127,是为了能够表示-128~127之间的所有数字,因为当表示小数时,我们需要负数阶数。
在我们潜意识中,float比int高级,精度也更高,因为float类型可以表示小数。实际上,在数值特别小的时候,float的精度确实比较高,然而当数值变大之后,float类型就会丢失精度。原因是float类型只有23位用来存储真实数据,float这23位存储的数据并不像int类型那样,以1为间隔均匀的分布在数轴上,而是有点类似于正态分布的样子,几种分布在0的两边,其表示的数值在0的两边特别密集,离零越远,则开始迅速稀疏,数与数之间的间隔开始变大,也就是说这23位的排列组合多被用到了0周围表示小数,当数字增大需要表示其他数字时,就没有二进制数列可用了,这就导致数列开始稀疏,数与数之间的间隔变得越来越大,如下面的示意:
int类型 -13423 -13422 -13421 -13420 ... -5 -4 -3 -2 -1 0 1 2 3 4 5 ... 13423 13424 13425 13426 float类型 -13000 -12000 -11000 -10000 ...-5,-5.999 -5.998 -5.997 ... -0.001 0 0.001 0.002 0.003 ... 10000 12000 13000
也就是说这种存储方法会让float在数轴上的范围增加,但是分布变得不均匀,进而在数值变大的时候发生严重的精度丢失,当我们给float赋值一个大数的时候,如123458,它可能会丢失成120000。float的有效精度为6到7位,意思是float超过6位数时,精确度就开始发生丢失了。因此,在进行小数计算时,我们通常不使用float类型,我们通常使用的是double类型。
我们经常使用各种编辑器,如记事本,而记事本的显示原理和存储原理又是什么呢?这是一个值得探究的问题。接下来我将详细解释笔记本或者其他编辑器的存储原理以及显示原理。
我们从计算机上获取的视觉信息,绝大部分是通过显示屏获取的,显示屏上显示的信息,都是由一个一个的像素点构成的,计算机上的像素点有成千上万个,像素点越多越密集,图像越清晰,我们买手机时通常要强调买高像素的,其实就是这个原理,计算机的长宽像素相乘就是总体的像素个数,或者说就是屏幕的分辨率,如1920 X 1080。
像素点有信息的区分,不同的像素点都有属于自己的坐标,通过对于坐标进行定位,我们可以控制像素点阵列,让像素点合力展现出形状出来,这和运动会中的举牌方阵原理非常相似。光学三元色是红绿蓝三种颜色,通过这几种颜色的配比,我们可以得到不同的颜色。其中亮度信息可以表现进三原色信息中,因为颜色的控制是由三原色配比展现的,其大小就可以控制亮度,如2,3,4表示一种颜色配比,那么20,30,40就可以表示亮度更高的该颜色,总体上讲,就是用比值大小表示颜色,用具体数字大小表示亮度信息。因此,再加上位置信息,我们就可以完完全全的控制一个像素了,如(142,456,2,3,5)就表示坐标位于(142,456)处的一个像素的颜色为(2,3,5),这两种信息合在一起总共五个数,我们需要五个数就可以表示一个像素的模样了,就这样,在内存中我们开辟一块空间,这一块空间每五个单位值就表示一个像素点,然后让这个空间等于像素数量乘每个像素点信息所需要的数位的大小,然后我们只需要控制这一串空间,然后将这一串空间中的信息发送给显示屏,显示屏按照相应的单位解析信息并控制像素点,就可以通过控制这块内存的方式来控制屏幕了。通常来说对于坐标的存储,通常使用short类型来存储,也就是xy坐标每个坐标占16bit,总共32bit,即坐标信息通常用32bit的内存空间来存储,而对于三原色,三元的范围都是从0到255,因此我们需要8位就可以进行三原色单个色的范围控制,因此三原色总共需要24bit,因此一个像素点信息,需要占用56bit的内存空间,也就是说一个像素点需要占用7字节的大小。在内存中我们有像素数量*7字节的大小来存储屏幕上的像素点信息,只要我们修改这一片内存空间,就可以修改屏幕上输出的图案,如今显卡上边自带显存,存储这一片点阵信息的空间转移到了显存之上,当然如果你使用的是核显,那还是得占用内存。
PS:在计算机中通常使用三原色的方式来表示颜色,也就是RGB(红绿蓝),我们用他们三个的配比来表示颜色以及亮度,实际上亮度信息已经包含进了颜色信息之中,如亮红色和暗红色,他们看上去不像是一个颜色,但其实是配比一样,参数大小成倍。其参数范围为RGB(0~255,0~255,0~255)。
因此我们知道了屏幕的显像原理:通过以56bit为单位的内存(显存)上的数据串与屏幕上的像素一一对应,将这段信息发送给显示屏后显示屏即可解析成像素的具体显示方式,修改这段信息,屏幕就会发生相应的变化,而屏幕上显示的图形,实际上就存在于这段内存上的信息串中。
对于一个字母的形状,实际上就是这段信息中的一小段确定的信息,我们可以称之为像素点阵信息,只要将这一段点阵信息加到像素信息中去,就可以在屏幕上得到一个相应的图形,相同的形状具有相同的点阵信息,因此人们把某些特定字符的点阵信息记下来,只要需要进行输出,就直接复制过来进行排列,发送到屏幕上就形成了相应的单词句子。
然而,一个字符的点阵信息往往需要很多个像素的信息,如果编写一个文件,这个文件中存的都是这种信息那未免太大了,因为一个像素就占了7个字节,一个几万字的论文,岂不是要几个g,因此人们为了更加方便的保存文件,让文件更小,开发出了编码这个概念。这个概念实际上很像活字印刷,其本质就是创建一套编码,每个编码可能也就有几位,编码的位数通常是8的倍数,这个和编码集的体量有关系,就这样,我们可以用一个固定位数的二进制数来代表一个点阵信息,人们为此写出了一个对照表,对照表中每一个特定的二进制数都会对应一个点阵信息,因此我们在书写文件时,写进去的不是点阵信息,而是编码,文件的物理形式其实是一串一串的编码,当我们想要查看文件时,系统就会对文件进行解码,解码过程其实就是按照固定的位数一个一个的截取,然后放进编码集中寻找与之对应的点阵信息,然后将点阵信息传送到显存之中,相对应的屏幕上就复原出我们当时写下的文件了。因此我们得到了记事本的基本原理:将点阵存在一个库中,用编码进行对应,在写记事本时,存储的是编码信息,在显示时,系统会处理检索这些编码并进行解码,解码就是在库中检索,然后将点阵信息发送至显示器进行显示。
当然这种方式也不是没有缺点,不同类型的点阵信息需要有不同的编码,在计算机的发展过程中,各国都出现了不同的编码集,如中国有表示汉字的GBK编码,俄罗斯有俄罗斯自己的编码,日本有日本字符的编码,韩国也有韩国自己的编码。这些编码中同一二进制数表示对应的形状可能不同,同时这些编码中的编码截取位数也可能不同,因此当我们对于一个文件使用了错误的解码方式时,就会错误的解码进而显示乱码。同时如果你想使用某个编码方式,你的电脑必须支持这种编码方式。
final是一个标识符,通常我们要修饰一个常量时,前边要加上final标识符,常量的命名方式是字母都为大写。同时这里有一个面试考点:被final修饰的类不可以被继承,被final修饰的方法不可被重写。final修饰的常量不可以被第二次赋值,final防止指令重新排序,保证多线程下的安全。 注意这里,相当重要!
pow方法和sqrt方法在写算法时经常用到,pow方法的使用方法为:
pow(2,4);//意为2的4次方 pow(x,n);//意为x的n次方
sqrt方法表示开根号,如:
sqrt(9);//相当于根号下9,结果为3
使用这两种方法都要引用数学方法库。
在Java中,可以进行强制类型转换,然而我们通常是将低精度类型转成高精度类型,而避免高精度类型向低精度类型转换,因为这样做会发生精度丢失,如我们可以将32位的float型转化成64位的double型,也可以把16位的short型转化成32位的int型,但是如果我们把16位的short型转换成8位的byte型,就会发生严重的精度丢失,如下:
short转化成byte: short八位:0101011001000010 强制转换 01010110(舍去) 01000010//将高八位舍去,留下了低八位
由此可见在转化过程中发生了严重的精度丢失,因此这种长位变量向短位变量的转化非常不可取,我们应该尽量避免这种不当的操作。
此知识点将单独进行解释,详情点击链接。
此知识点将单独进行解释,详情点击链接。