🐿️ 包package
type
status
date
slug
summary
tags
category
icon
password
Property
 
在现实中,如果小明写了一个Person类,小红也写了一个Person类,现在,小白既想用小明的Person,也想用小红的Person,怎么办?
如果小军写了一个Arrays类,恰好JDK也自带了一个Arrays类,如何解决类名冲突?
 
 
Java定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是包名.类名
例如:小明的Person类存放在包ming下面,完整类名是ming.Person;小军的Arrays类存放在包mr.jun下面,完整类名是mr.jun.Arrays;JDK的Arrays类存放在包java.util下面,完整类名是java.util.Arrays
 
 
在定义class的时候,需要在第一行声明这个class属于哪个包,小明的Person.java文件:
小军的Arrays.java文件:
🐿️ 作用域
type
status
date
slug
summary
tags
category
icon
password
Property
 
我们经常看到publicprotectedprivate这些修饰符。在Java中,这些修饰符可以用来限定访问作用域

public

定义为publicclassinterface可以被其他任何类访问:
上面的Hellopublic,因此,可以被其他包的类访问:
定义为publicfieldmethod可以被其他类访问,前提是首先有访问class的权限。
 
 

private

定义为privatefieldmethod无法被其他类访问:
🐿️ 内部类
type
status
date
slug
summary
tags
category
icon
password
Property
 
Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:
  • Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;
  • Static Nested Class是独立类,但拥有Outer Class的private访问权限。
 
 

Inner Class

如果一个类定义在另一个类的内部,这个类就是Inner Class:
上述定义的Outer是一个普通类,而Inner是一个Inner Class,它与普通类有个最大的不同,就是Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。示例代码如下:
要实例化一个Inner,必须首先创建一个Outer的实例,然后,调用Outer实例的new来创建Inner实例:
🐿️ classpath和jar
type
status
date
slug
summary
tags
category
icon
password
Property
 
 

classpath

什么是classpath
classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class
因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个abc.xyz.Hello的类,应该去哪搜索对应的Hello.class文件。
所以,classpath就是一组目录的集合,它设置的搜索路径与操作系统相关。例如,在Windows系统上,用;分隔,带空格的目录用""括起来,可能长这样:
在Linux系统上,用:分隔,可能长这样:
 
现在假设classpath.;C:\work\project1\bin;C:\shared,当JVM在加载abc.xyz.Hello这个类时,会依次查找:
🐿️ 模块
type
status
date
slug
summary
tags
category
icon
password
Property
 
从Java 9开始,JDK引入了模块(Module)
.class文件是JVM看到的最小可执行文件,而一个大型程序需要编写很多Class,并生成一堆.class文件,很不便于管理,所以jar文件就是class文件的容器。
在Java 9之前,一个大型Java程序会生成自己的jar文件,同时引用依赖的第三方jar文件,而JVM自带的Java标准库,实际上也是以jar文件形式存放的,这个文件叫rt.jar,一共有60多M。
如果是自己开发的程序,除了一个自己的app.jar以外,还需要一堆第三方的jar包,运行一个Java程序,一般来说,命令行写这样:
如果漏写了某个运行时需要用到的jar,那么在运行期极有可能抛出ClassNotFoundException
所以,jar只是用于存放class的容器,它并不关心class之间的依赖。
 
从Java 9开始引入的模块,主要是为了解决“依赖”这个问题。如果a.jar必须依赖另一个b.jar才能运行,那应该给a.jar加点说明啥的,让程序在编译和运行的时候能自动定位到b.jar,这种自带“依赖关系”的class容器就是模块。
为了表明Java模块化的决心,从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识,可以在$JAVA_HOME/jmods目录下找到它们:
  • java.base.jmod
🦔 包装类型
type
status
date
slug
summary
tags
category
icon
password
Property
 
Java的数据类型分两种:
  • 基本类型:byteshortintlongbooleanfloatdoublechar
  • 引用类型:所有classinterface类型
 
引用类型可以赋值为null,表示空,但基本类型不能赋值为null
 
 
如何把一个基本类型视为对象(引用类型)?
比如,想要把int基本类型变成一个引用类型,可以定义一个Integer类,它只包含一个实例字段int,这样,Integer类就可以视为int的包装类(Wrapper Class):
🦔 String
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
实际上字符串在String内部是通过一个char[]数组表示的,因此按下面的写法也是可以的:
因为String太常用了,所以Java提供了"..."这种字符串字面量表示方法。
 
Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段,以及没有任何修改char[]的方法实现的。
 

字符串比较

当比较两个字符串是否相同时,要特别注意实际上是想比较字符串的内容是否相同,必须使用equals()方法而不能用==
🦔 StringBuilder
type
status
date
slug
summary
tags
category
icon
password
Property
 
Java编译器对String做了特殊处理,可以直接用+拼接字符串。
虽然可以直接拼接字符串,但在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大部分字符串都是临时对象,不但浪费内存,还会影响GC效率。
 
为了能高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象:
 
StringBuilder还可以进行链式操作:
进行链式操作的关键是,定义的append()方法会返回this,这样,就可以不断调用自身的其他方法。
 
🦔 StringJoiner
type
status
date
slug
summary
tags
category
icon
password
Property
 
很多时候,拼接的字符串像这样:
类似用分隔符拼接数组的需求很常见,所以Java标准库还提供了一个StringJoiner来干这个事:
StringJoiner的结果少了前面的"Hello "和结尾的"!"!遇到这种情况,需要给StringJoiner指定“开头”和“结尾”:
 

String.join()

String还提供了一个静态方法join(),这个方法在内部使用了StringJoiner来拼接字符串,在不需要指定“开头”和“结尾”的时候,用String.join()更方便:
🦔 JavaBean
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
在Java中,有很多class的定义都符合这样的规范:
  • 若干private实例字段;
  • 通过public方法来读写实例字段。
 
例如:
如果读写方法符合以下这种命名规范:
那么这种class被称为JavaBean
上面的字段是xyz,那么读写方法名分别以getset开头,并且后接大写字母开头的字段名Xyz,因此两个读写方法名分别是getXyz()setXyz()
🦔 枚举类
type
status
date
slug
summary
tags
category
icon
password
Property
 
如果希望定义周一到周日这7个常量,可以用7个不同的int表示:
 
也可以把常量定义为字符串类型:
 
无论是int常量还是String常量,使用这些常量来表示一组枚举值的时候,有一个严重的问题就是,编译器无法检查每个值的合理性。例如:
上述代码编译和运行均不会报错,但存在两个问题:
  • 注意到Weekday定义的常量范围是0~6,并不包含7,编译器无法检查不在枚举中的int值;
  • 定义的常量仍可与其他变量比较,但其用途并非是枚举星期值。
🦔 记录类
type
status
date
slug
summary
tags
category
icon
password
Property
 
使用StringInteger等类型的时候,这些类型都是不变类,一个不变类具有以下特点:
  1. 定义class时使用final,无法派生子类
  1. 每个字段使用final,保证创建实例后无法修改任何字段
 
假设希望定义一个Point类,有xy两个变量,同时它是一个不变类,可以这么写:
为了保证不变类的比较,还需要正确覆写equals()hashCode()方法,这样才能在集合类中正常使用,代码写起来很繁琐。
 

record

从Java 14开始,引入了新的Record类。定义Record类时,使用关键字record。把上述Point类改写为Record类: