Java语言入门
1. Java入门
1.1. Java语言发展历史
版本 | 年份 | 特性 |
---|---|---|
1.0 | 1996 | 语言本身,约8.3万个网页应用Java技术 |
1.1 | 1997 | 内部类 |
1.2 | 1998 | strictfp关键字 |
1.3 | 2000 | 无 |
1.4 | 2002 | 断言 |
5.0 | 2004 | 添加泛型,for each循环,自动装箱,注解 |
6 | 2006 | 改进性能,增强类库 |
7 | 2011 | 2009年Oracle收购Sun公司 switch,钻石操作符,二进制字面量,异常处理改进 |
8 | 2014 | lambda表达式,包含默认方法接口,流和日期/时间库 |
9 | 2017 | |
10 | 2018.3 | |
11 | 2018.9 | |
12 | 2019.3 | |
13 | 2019.9 | |
14 | 2020.3 | |
15 | 2020.9 |
1.2. Java语言应用领域
- Java Web开发:后台开发
- 大数据开发
- Android应用程序开发
1.3. Java语言特性
1.3.1. 简单性
java语言是c++的“纯净”版本。没有头文件,指针运算,结构,联合,操作符重载,虚基类等。— 《Java核心技术卷I》
java支持开发能够在小型机器上独立运行的软件,基本的解释器以及类支持大约仅为4KB,加上基础的标准类库和对线程的支持大约需要175KB。这是一个了不起的成就。— 《Java核心技术卷I》
1.3.2. 面向对象
java把重点放在数据和对象的接口上。如木匠,“面向对象”木匠关注的是椅子,“面向过程”木匠关注的是使用的工具。
1.3.3. java面向对象特征
封装
:高内聚,低耦合一般使用private访问权限
提供相应的get/set方法来访问相关属性,方法的访问权限通常为public,以提供对属性的赋值和读取
一些只用于本类的辅助性方法可以用private修饰,其他类希望调用的方法用public修饰
继承
:实现代码复用多态
:继承,重写方法,父类引用指向子类对象父类变量为编译类型
子类对象为运行时类型
1.3.4. 分布式
java可以处理像HTTP,FTP之类的TCP/IP协议,能够通过URL打开的访问网络上的对象
1.3.5. 安全性
java适用于网络/分布式环境。
使用java可以构建防病毒,防篡改的系统。
防范运行时堆栈溢出。
防范未经授权读写文件。
1.3.6. 平台无关性
java数据类型具有固定的大小,如java的int永远是32位整数。消除了代码移植时数据溢出等问题。
java二进制以固定的格式进行存储和传输,消除了字节顺序的困忧。
java编译器生成与计算机体系结构无关的字节码指令,通过特定的java虚拟机执行字节码文件。
1.3.7. 支持多线程
带来更好的交互响应和实时行为
1.4. 比较其他语言
1.4.1. Java
由美国SUN公司发明于1995年,是目前业界应用最广泛,使用人数最多的语言,连续多年排名世界第一,称之为计算机语言界的英语。被广泛应用于企业级软件开发,安卓移动开发,大数据云计算等领域,几乎涉及IT所有行业。
1 | public class HelloWorld{ |
1.4.2. C
诞生于1972年,现代高级语言的鼻祖,由贝尔实验室发明。C语言是人们追求结构化,模块化,高效率的“语言之花”。在底层编程,比如嵌入式,病毒开发等应用,可以替代汇编语言来开发系统程序。在高层应用,也可以开发从操作系统(Unix,Linux,Windows都基于C语言开发)到各种应用软件。
1 |
|
1.4.3. C++
作为c语言的扩展,是贝尔实验室于80年代推出的,C++是一种混合语言,既可以实现面向对象编程,也可以开发c语言面向过程风格的程序。
C++是编译性语言,使用编译器,针对特定的操作系统,将源代码一次性翻译成可被平台硬件执行的机器码,并包装成该平台所能识别的可执行程序的格式,程序能独立运行。运行效率高,但不能在其他操作系统上运行,又需要重新编译,不易于移植。
c++是面向过程编程的,java是面向对象编程的。
c++中对于内存的分配需要程序显式地管理,用到析构函数。而java提供了垃圾回收机制来实现垃圾的自动回收
c++存在预处理,宏定义等,而java的操作都集中在类中
1 |
|
1.4.4. C#
c#是微软公司发布的一种面向对象的,运行于.NET Framework之上的高级程序设计语言。
C#在基于windows操作系统的应用开发这一领域在取代C++,占据主导地位。Unity3D开发游戏时,使用C#和javascript
java和c#都摒弃了c++函数及其参数的const修饰,宏替换,全局变量和全局函数等,但c#仅局限于windows平台,windows平台有大量的c#基类。对于java的平台独立性来说,c#不利于移植于其他平台。
1.4.5. Python
发明于1989年,语法结构简单,易学易懂;python具有丰富和强大的库。常被称之为胶水语言,能够把用其他语言制作的各种模块很轻松的联结在一起。广泛应用于图形处理,科学计算,web编程,多媒体应用,引擎开发,尤其是大热的人工智能和机器学习。
是面向对象的解释性脚本语言,拥有大量的类库。
解释性语言是使用解释器,对源代码逐行解释成特定平台的机器码并立即执行。把编译和解释过程混合到一起同时完成。每次执行都需要进行一次编译,不能离开解释器独立运行。
1.4.6. JavaScript
脚本语言,广泛应用于web应用开发,应用范围越来越大,重要性越来越高。流行的H5开发的核心就是JavaScript语言。
1 | <script> |
1.5. Java语言版本
J2ME(Micro Edition) - 控制移动设备和信息家电等有限存储设备,android开发
J2SE(Sandard Edition) - java技术核心,桌面或简单服务器应用的java平台,QQ等
J2EE(Enterpirse Edition) - 企业应用开发相关,服务器端应用
Open JDK - javaSE的免费开源实现
1.6. java包下载
https://www.oracle.com/cn/java/technologies/javase-downloads.html
1.6.1. JDK
java开发工具包,包括java编译器(javac),运行时环境,常用类库,JRE,开发工具(javadoc和jdb)。
它能够创建和编译程序。
1.6.1.1. Oracle JDK
Oracle JDK 是 OpenJDK 的一个实现,并不是完全开源的
Oracle JDK 比 OpenJDK 更稳定。OpenJDK 和 Oracle JDK 的代码几乎相同,但 Oracle JDK 有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,我建议您选择 Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用 OpenJDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到 Oracle JDK 就可以解决问题
在响应性和 JVM 性能方面,Oracle JDK 与 OpenJDK 相比提供了更好的性能
Oracle JDK 不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本
Oracle JDK 使用 BCL/OTN 协议获得许可,而 OpenJDK 根据 GPL v2 许可获得许可
- BCL 协议(Oracle Binary Code License Agreement): 可以使用 JDK(支持商用),但是不能进行修改。
- OTN 协议(Oracle Technology Network License Agreement): 11 及之后新发布的 JDK 用的都是这个协议,可以自己私下用,但是商用需要付费
Oracle JDK 各个版本所用的协议
Oracle JDK版本 | BCL协议 | OTN协议 |
---|---|---|
6 | 最后一个公共更新6u45之前 | |
7 | 最后一个公共更新7u80之前 | |
8 | 8u201/8u202之前 | 8u211/8u212之后 |
9 | ☑️ | |
10 | ☑️ | |
11 | ☑️ | |
12 | ☑️ |
1.6.1.2. OpenJDK
OpenJDK 项目主要基于 Sun 捐赠的 HotSpot 源代码,完全开源。
OpenJDK 被选为 Java 7 的参考实现,由 Oracle 工程师维护
关于 JVM,JDK,JRE 和 OpenJDK 之间的区别,Oracle 博客帖子在 2012 年有一个更详细的答案:
问:OpenJDK 存储库中的源代码与用于构建 Oracle JDK 的代码之间有什么区别?
答:非常接近 - 我们的 Oracle JDK 版本构建过程基于 OpenJDK 7 构建,只添加了几个部分,例如部署代码,其中包括 Oracle 的 Java 插件和 Java WebStart 的实现,以及一些闭源的第三方组件,如图形光栅化器,一些开源的第三方组件,如 Rhino,以及一些零碎的东西,如附加文档或第三方字体。展望未来,我们的目的是开源 Oracle JDK 的所有部分,除了我们考虑商业功能的部分。
1.6.2. JRE
java运行时环境。
包括JVM,类加载器,字节码校验器等。
它是运行已编译java程序所需的所有内容的集合,不能用于创建新程序。
如果只是为了运行一下java程序,只需要安装JRE就可以。需要进行一些java编程工作,则需要安装JDK。
1.6.3. JVM
执行字节码的虚拟计算机,定义了指令集,寄存器集,结构栈,垃圾收集堆,内存区域。负责将java字节码解释运行,边解释边运行。不同操作系统不同虚拟机,从而屏蔽了底层运行平台的差别,实现了“一次编译,随处运行”,jvm是java实现跨平台的核心机制。
JVM并不是只有一种,只要满足JVM规范,每个公司,个人都可以开发自己专属JVM。
除了常用的HotSpot VM外,还有J9 VM,Zing VM,JRockit VM等VM。
常见的JVM对比:https://en.wikipedia.org/wiki/Comparison_of_Java_virtual_machines
官方各个JDK版本对应的JVM规范:https://docs.oracle.com/javase/specs/index.html
1.6.4. JDK文件夹组成
– bin 工具命令
– db 数据库
– include 平台特定头文件
– jre JRE环境
– lib 工具命令实际执行程序
– javafc-src.zip
– README,LICENSE说明文档
1.6.5. 配置环境变量-mac
下载的是.dmg文件
path是操作系统执行命令时,所要搜寻的路径
1 | open .bash_profile |
1.6.6. 配置环境变量-Linux
下载的是.tar.gz文件
解压
1 | tar zxvf xxx.tar.gz |
放到/usr/local/jdk7
1 | cp -r xxx /usr/local/jdk8 |
配置环境变量
1 | nano /etc/profile |
变量
1 | JAVA_HOME=/usr/local/jdk8 |
使其生效
1 | source profile |
验证
1 | java -v |
1.7. 编写第一个Java程序
1 | public class HelloWorld{ |
class 类是构建所有java应用程序的构建块
main是jvm访问入口标识
void表示方法没有返回值,main方法正常退出,退出代码为0,表示成功运行了程序
static 说明main方法是一个静态方法,即方法中的代码存储在静态存储区,只要类被加载后,就可以使用该方法而不需要通过实例化对象来访问。类还没有实例化,并不能通过对象调用方法,static表示该方法属于类,可以直接通过类.main()直接访问,JVM启动后就是按照这个方法的签名来查找方法的入口地址。
public 说明方法是类可见,包可见的,外部可以调用
String[] 数组用于和程序员进行交互,java HelloWorld a b ==>String[0]=a,String[1]=b;
必须保证main()返回值为void,并有static与public修饰
故main()可以用final,synchronized修饰
文件的命名必须和public class类名相同,一个java文件只能有一个public class
System.out.println("Hello World");
表示把文本行输出到控制台上
在java中,每个句子必须用分号结束
.
用于调用方法,等价于函数调用
1.8. Java语言运行机制
1 | javac xx.java //编译 |
java语言先编译再解释成机器码。
java源码经编译器编译成.class,生成的是与平台无关的字节码,字节码只能通过java虚拟机jvm解释成特定平台的机器码。
1.8.1. 字节码
JVM可以理解的代码就叫做字节码(.class文件),它不面向任何特定的处理器,只面向java虚拟机。java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时保留了解释型语言可移植的特点。所以,java程序运行时相对来说还是高效的。而且,由于字节码并不针对一种特定的机器,因此,java程序无须重新编译便可在多种不同操作系统的计算机上运行。

.class->类装载器->字节码校验器->解释器
需要加载字节码文件,通过解释器逐行解释执行,从而这种方式执行速度会相对比较慢。会有一些方法和代码块经常被调用而被称为热点代码,从而引进了JIT(just in time compilation)编译器,属于运行时编译,在完成第一次编译后,会将字节码对应的机器码保存下来,下次可以直接使用,机器码的运行效率是高于java解释器的。所以常说java是编译与解释共存的语言。
HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。
JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。
JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了 JIT 预热等各方面的开销。
JDK 支持分层编译和 AOT 协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。
1.8.2. java是编译与解释共存的语言
编译型
编译型语言 会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等
解释型
解释型语言 会通过解释器一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。
java则是采用即时编译技术,已经缩小了这两种语言间的差距。这种技术混合了编译语言与解释型语言的优点,它像编译语言一样,先把程序源代码编译成字节码。到执行期时,再将字节码直译,之后执行。
2. Java语法
2.1. Java注释
当项目结构复杂或编写的代码有段时间了,为了方便理解代码意思,可以对代码进行注释。
注释中的内容并不会被执行,在编译期就会被编译器抹掉。
写程序时随手加上注释是一个非常好的习惯。
《clean code》书中指出:
代码的注释不是越详细越好。实际上好的代码本身就是注释,我们要尽量规范和美化自己的代码来减少不必要的注释。
若编程语言足够有表达力,就不需要注释,尽量通过代码来阐述。
注释方式://
,/**/
, /** */
//
用于单行注释
/**/
可用于多行注释
/** */
可用于自动生成javadoc文档
2.2. Java关键字
关键字是指有专门用途的字符串。属于已经被赋予特殊含义,只能用于特定地方的标识符。
分类 | 关键字 | ||||||
---|---|---|---|---|---|---|---|
访问控制 | private | protected | public | ||||
类,方法和变量修饰符 | abstract | class | extends | final | implements | interface | native |
new | static | strictfp | synchronized | transient | volatile | ||
程序控制 | break | continue | return | do | while | if | else |
for | instanceof | switch | case | default | |||
错误处理 | try | catch | throw | throws | finally | ||
包 | import | package | |||||
基本类型 | boolean | byte | char | double | float | int | long |
short | null | true | false | ||||
变量引用 | super | this | void | ||||
保留字 | goto | const |
2.3. Java标识符
标识符用来给变量,类,方法以及包进行命名。
标识符必须以字母,下划线_,美元符号$组成
标识符其他部分可以是字母,下划线,美元符和数字的任意组合
标识符大小写敏感,长度无限制
不可以使用关键字和保留字,但能包含关键字和保留字
标识符不能包含空格
不建议使用汉字命名
2.4. Java命名规范
包名:xxxyyyzzz
类名,接口名:XxxYyyZzz
变量名,方法名:xxxYyyZzz
常量名:XXX_YYY_ZZZ
2.5. Java变量
变量 代表可操作的存储空间,空间位置确定,但内容不定。
要素包括变量名,变量类型和作用域,甚至变量值。
变量在使用前必须对其声明,只有在变量声明之后,才能为其分配相应长度的存储空间。
变量的作用域在{}
内
变量声明 double a = 0.3;
每个声明以分号结束。
变量必须是一个以字母开头并由字母,数字组成的序列,字母包括A~Z,a~z,_,$
变量可分为成员变量(实例变量,类变量),局部变量(形参,方法局部变量,代码块局部变量)
2.6. Java常量
在java中关键字final指示常量,表示只能被赋值一次
1 | public static final int VALUE = 10; |
2.7. Java数据类型
java是强类型语言,每一个变量必须声明为一种类型。
java中共有8种基本数据类型和3种引用类型。
基本数据类型包括4种整型(byte,short,int,long),2种浮点型(float,double),1种表示Unicode编码的字符单元的字符类型char,1种用于表示真值的boolean值。
引用类型包括类,接口,数组。
基本数据类型直接存放在java虚拟机栈种的局部变量表中,而基本数据类型对应的包装类属于对象类型,对象实例存在于堆中。
java没有无符号(unsigned)形式
null不是合法的object实例,编译器不会分配内存,它仅仅表示当前引用类型不指向任何对象
2.7.1. 整型
类型 | 存储需求 | 取值范围 | 默认值 | 包装类 |
---|---|---|---|---|
byte | 1字节 = 8位 | -128~127 | 0 | Byte |
short | 2字节=16位 | -32768~32767 | 0 | Short |
int | 4字节=32位 | -2147483648~2147483647 2x$10^9$ | 0 | Int |
long | 8字节=64位 | -9x$10^{18}$~9x$10^{18}$ | 0L | Long |
十六进制有前缀0x,0x010=16
八进制有前缀0,010=8
二进制有前缀0b,0b010=2
2.7.2. 浮点型
类型 | 存储需求 | 取值范围 | 默认值 | 包装类 |
---|---|---|---|---|
float | 4字节 | 32位 1 8 11,6~7位精确 | 0.0f | Float |
double | 8字节 | 64位 1 11 52,15-16位精确 | 0.0(浮点数默认类型) | Double |
浮点数遵循IEEE754规范,浮点数值不适合存在舍入误差的计算
为什么浮点数 float
或 double
运算的时候会有精度丢失的风险呢?
这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。这也就是解释了为什么浮点数没有办法用二进制精确表示。
就比如说十进制下的 0.2 就没办法精确转换成二进制小数:
1 | // 0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止, |
2.7.3. 字符型char
类型 | 存储需求 | 取值范围 | 默认值 | 包装类 |
---|---|---|---|---|
char | 2字节=16位 | 0-65536 | \u0000 | Character |
形式
:字符常量是单引号引起的一个字符,字符串常量是双引号引起的0个或若干个字符
含义
: char类型的值可以表示为十六进制值,取值范围为\u0000-\uffff。字符常量可以参加表达式运算,字符串常量代表一个地址值,表示其在内存中存放位置。
表现形式
:
- char ch = ‘A’;
转义字符
:特殊含义的字符,如\n
表示换行- 直接使用 Unicode 值来表示字符型常量。
\u000a
=\n
当计算机保存某个字符时,将该字符的编号转换成二进制码,其中字符集就是所有字符的编号组成的集合。
汉字:默认使用Unicode编码方式,一个字符占2字节
英文占1字符,中文占2字符
判断汉字:String.getByte().length() == String.length()
2.7.4. 布尔型boolean
类型 | 存储需求 | 取值范围 | 默认值 | 包装类 |
---|---|---|---|---|
boolean | 4字节,32位 而boolean数组是1字节,8位 |
false | Boolean |
boolean有两个值:true和false,用来判定逻辑条件。不同于c++语言,整数值和布尔值不能相互转换。
1 | boolean flag = true; |
flag==1
是不对的
但在c++中是可以的
1 | bool flag = true; |
2.7.5. 数值类型之间的转换
当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型
当byte,char,short三种类型的变量做运算时,结果为int型
实线表示无信息丢失的转换,虚线表示有精度损失的转换
byte/short/char在表示范围内,javac编译时自动会隐含进行类型转换
1 | //字面量5是一个int类型,int->byte 必须进行类型转换 |
变量赋值时,右侧表达式中全都是常量,javac直接将若干常量表达式计算得到结果
1 | short result = 5 + 8; |
2.7.6. 强制类型转换
1 | double a = 3.14; |
会截断小数部分将浮点值转换为整型
当试图将一个数值从一种类型强制转换为另一种类型,又超出目标类型的表示范围,结果会截断成一个完全不同的值
2.7.7. 包装类
java中对应每个基本数据类型都有包装类,把数据装成对象
Integer,Float,Double,Long,Short,Byte,Character,Void,Boolean 均为不可变类
2.7.7.1. Integer
1 | public final class Integer extends Number implements Comparable<Integer> { |
2.7.7.2. Float
1 | public final class Float extends Number implements Comparable<Float> { |
2.7.7.3. Double
1 | public final class Double extends Number implements Comparable<Double> { |
2.7.7.4. Long
1 | public final class Long extends Number implements Comparable<Long> { |
2.7.7.5. Short
1 | public final class Short extends Number implements Comparable<Short> { |
2.7.7.6. Byte
1 | public final class Byte extends Number implements Comparable<Byte> { |
2.7.7.7. Character
1 | public final class Character implements java.io.Serializable, Comparable<Character>{ |
2.7.7.8. Void
1 | public final class Void { |
2.7.7.9. Boolean
1 | public final class Boolean implements java.io.Serializable,Comparable<Boolean>{ |
2.7.8. 装箱,拆箱
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
1 | //装箱 |
2.7.9. 缓存池问题
1 | Integer x = new Integer(2); |
【分析】:new Integer(2) 每次创建新对象;Integer.valueOf(2) 使用缓存池取的是同一个对象的引用。
Jdk1.8
1 | public static Integer valueOf(int i) { |
Integer 缓存池的大小默认为 -128~127。cache[256]
1 | private static class IntegerCache { |
2.7.10. 上界可调
启动JVM配置
1 | -XX:AutoBoxCacheMax=<size> |
该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界。
1 | public class Test { |
编译
1 | javac Test.java |
1 | javac Test.java |
2.7.11. 其他数据类型的缓存
Short [255]
1 | public static Short valueOf(short s) { |
Byte [256]
1 | static final Byte cache[] = new Byte[-(-128) + 127 + 1]; |
Character [128]
1 | static final Character cache[] = new Character[127 + 1]; |
Boolean
1 | public static Boolean valueOf(boolean b) { |
两种浮点数类型的包装类 Float
,Double
并没有实现常量池技术。
2.8. 大数值
任意长度数字序列数值
###大整数BigInteger
1 | //BigInteger.valueof(long x) |
2.8.1. 大实数BigDecimal
2.8.1.1. 初始化
1 | BigDecimal bg = BigDecimal.valueof(long val); |
2.8.1.2. 加减乘除
add
方法用于将两个BigDecimal
对象相加subtract
方法用于将两个BigDecimal
对象相减multiply
方法用于将两个BigDecimal
对象相乘divide
方法用于将两个BigDecimal
对象相除
1 | BigDecimal a = new BigDecimal("1.0"); |
其中divide
方法
1 | /* |
2.8.1.3. 比较
a.compareTo(b)
: 返回 -1 表示 a
小于 b
,0 表示 a
等于 b
, 1 表示 a
大于 b
。
1 | BigDecimal a = new BigDecimal("1.0"); |
2.8.1.4. 保留几位小数
通过 setScale
方法设置保留几位小数以及保留规则。保留规则有挺多种,不需要记,IDEA 会提示。
1 | BigDecimal m = new BigDecimal("1.255433"); |
2.8.1.5. 返回值
1 | public double doubleValue(){} |
2.9. 数组
引用类型。存储同一类型值的有序集合。通过一个整型下标可以访问数组中的每一个值。
- 长度固定。一旦创建,大小不可变。
- 元素类型相同。
- 数组类型可以是任意数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也是对象,数组元素就是对象属性。
2.9.1. 初始化
1 | //定义数组长度 |
java没有指针运算,即不能通过array+1得到数组下一个元素
java在定义数组时,并不会给数组元素分配存储空间,[]中不需要指定数组的长度
2.9.2. Arrays 工具类
java.util.Arrays
1 | //数组所有值拷贝到新数组 |
输出
1 | int table[n][m]; |
2.10. 运算符
+-*/表示加减乘除
%
结果的符号与被模数的符号相同
2.10.1. strictfp关键字
double w = x * y / z
默认情况下,允许Intel处理器将中间结果存储在80位寄存器,最终结果截断为64位
strictfp关键字则使用严格的浮点计算,可能出现溢出
2.10.2. 结合赋值和运算符 +=,/=,-=,*=
x+=4
; == x=x+4;
1 | short short1 = 1; |
2.10.3. 自增与自减运算符
n++ 表示变量n先参与运算,运算完再+1
++n 表示n先+1,再参与运算
用一句口诀就是:“符号在前就先加/减,符号在后就后加/减”。
2.10.4. 关系运算符
<,>,>=,<=,&&,||,!,!=,?:
if(a>1&&a<10)
表示判断a是否大于1,小于10
if(a>1||a>10)
表示判断a是否大于1,或者大于10,||短路或,前表达式为true时,不计算后一个表达式
int a = x<y?x:y;
表示x小于y的话,a=x,否则的话a=y
2.10.5. 位运算符
&
and与:1&1=1,1&0=0,0&0=0,位运算符并,逻辑运算符作用同&&,但不短路,计算前后两个表达式
|
or或:1|0=0 , 1|1=1,0|0=0,位运算符或,逻辑运算符作用同||,不短路,计算前后两个表达式
^
xor异或:1^0=1,0^0=0,1^1=0,位运算符异或,逻辑运算符只有当两个表达式结果不同时才返回true
~
not非:0=1,1=0
>>
右移:符号位填充高位,相当于除以2
>>>
右移:0填充高位
<<
左移:0填充低位,相当于乘以2
移位运算符右操作符要完成模32运算,1<<35 = 1<<3 = 8
2.11. 枚举类型
变量取值只在一个有限集合内,可以自定义枚举类型。
1 | enum Size{SMALL,MEDIUM,LARGE}; |
Size类型变量只能存储这个类型声明中国给定的某个枚举值
以下部分需要学习面向对象后理解
2.11.1. 自定义枚举类
1 | public class Season { |
运行结果:
1 |
|
2.11.2. enum类
1 | public enum Size{ |
2.11.3. 实现接口
不同枚举实现的方法不同
1 | interface info{ |
测试:
1 |
|
2.12. 字符串
2.12.1. String不可变字符串
定义为String e = "";
从概念上来说,字符串就是Unicode字符序列
从源码上来说,String内部实现是字符数组
1 | public final class String implements java.io.Serializable, Comparable<String>, CharSequence { |
2.12.1.1. String 真正不可变的原因
final关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的基本数据类型变量不能改变值,修饰的引用类型不能再指向其他对象,final关键字修饰的数组保存的字符串其实是可变的。
真正导致String不可变是因为保存字符串的数组被final修饰且为私有的,并且String类没有提供修改这个字符串的方法;String被final修饰导致不能被继承,避免了子类破坏String不可变。
2.12.1.2. 常用方法
查看在线API:https://docs.oracle.com/javase/8/docs/api/
1 | String str.replace("",""); |
String与其他数据之间的转换
1 | //char[] -> string |
2.12.1.3. 空串与null串
1 | String str = null; |
null表示目前没有任何对象与该变量关联
length()返回采用UTF-16编码表示的给定字符串所需要的代码单元数量
2.12.1.4. 源码分析
Java 8
1 | public final class String implements java.io.Serializable, Comparable<String>, CharSequence { |
value数组声明为final,初始化后不能被其他数组引用,保证String不可变。
不可变
- 保证String的hash值不变,在HashMap中当key时保证键值
- 保证String从常量池中取得字符串常量
- 作为网络连接参数,确保双方数据一致性
- 保证多线程的安全性
源码分析
1 | String str="Hello"; |
==
1 | StringBuilder su= new StringBuilder("Hello"); |
2.12.1.5. 存储机制
1 | String s1="abc"; |
s1=s2=s3!=s4
常量池中存放abc字符串常量,s1,s2都指向常量池中的abc
s3的ab,c同样在编译期就被解析成一个字符串常量abc,s3指向abc
new String(),堆中生成new出来的对象,对象中value[]指向常量池中的abc,栈中的s4指向堆中的对象
常量池:在编译期就确定的被保存在已编译的.class文件中的一些数据。
1 | String a = "abc"; |
常量与常量的拼接结果在常量池中,而且常量池中不存在相同的内容常量
只要出现变量,结果就会存入堆中
拼接结果调用intern()方法,返回值放在常量池中
跟输出形式关联
1 | String str=String.format("%d",age); |
2.12.1.6. 练习
1 | char c = 'a'; |
2.12.1.7. 可变长参数
从java5开始,java支持可变长参数,即允许在调用方法时传入不定长度的参数。
1 | public void method(String... args){} |
遇到方法重载的情况怎么办呢?会优先匹配固定参数还是可变参数的方法呢?
会优先匹配固定参数的方法,因为固定参数的方法匹配度更高
1 |
|
结果
1 | 一个参数 |
2.12.2. StringBuilder
每次连接字符串都会构建一个新的String对象,既耗时,又浪费空间,使用StringBuilder可以避免这个问题
调用
1 | StringBuilder str = new StringBuilder(); |
append()方法返回的是创建的StringBuilder对象,所以可以无限调用
源码
1 | public final class StringBuilder extends AbstractStringBuilder |
2.12.3. StringBuffer
作为StringBuilder的前身,因采用多线程方式执行添加或删除字符操作故效率低
1 | public final class StringBuffer extends AbstractStringBuilder |
2.13. 输入输出
java.util.Scanner类实现输入操作
1 | Scanner sc=new Scanner(System.in);//InputStream |
System.out输出到控制台
1 | int a = 10; |
2.14. 控制流程
2.14.1. 条件语句
1 | if(condition) |
switch语句将从选项匹配的case标签处开始执行直到遇到break语句,如果没有相匹配的case标签,如果有default语句,则执行这个语句。
case标签可以是char,byte,short,int,String,枚举常量
1 | int choice = 1; |
2.14.2. 循环语句
1 | while(condition){ |
先判断condition,为true时执行一次语句,再判断条件,持续这个操作,直到条件为false时,退出循环。
如果一开始条件就为false,则语句一次也不会执行
1 | do{ |
无论条件是否满足都先执行一次语句,再判断条件,然后持续判断执行操作,直到条件为false时,退出循环。
1 | for(int i=0;i<=10;i++){ |
初始化变量i=0,再判断i值是否满足条件,条件满足情况下执行语句,即输出i值,最后将i值加1,即为2,将i值判断是否满足条件,条件满足情况下执行语句……持续到i值不满足条件后完成for循环

2.14.3. 中断
在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。但是,有时候可能需要在循环的过程中,当发生了某种条件之后 ,提前终止循环
break
退出当前整个循环
continue
跳过本次执行,继续下一次
1 | //当i=1时退出循环,故输出0 |