转载

你不知道的 Java 基础

您好,欢迎关注我的专栏,本专栏从Java基础、集合、内存GC、IO、多线程、JVM以及性能优化7个方面剖析Java,旨在帮助有一定Java开发经验的程序员提高自己。如有问题,可在我的专栏底部给我留言,我会尽我的能力给你解答。

Java 基础

本文会介绍一些鲜为人知的Java基础知识,包括 基本数据类型String自动装箱正则表达式label语句 等等。

基本数据类型

学过Java的人都知道Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。

这里有个表可以整体的看一下:

其中整数类型 字符型 浮点型 布尔型 引用类型
byte (1字节)
short(2字节)
int    (4字节)
long (8字节)
char(2字节) float    (4字节)
double(8字节)
boolean(1字节)
接口
数组
Null

整数类型

需要注意一下各个类型的取值范围:

byte 的取值范围:-128~127(-2的7次方到2的7次方-1)
short 的取值范围:-32768~32767(-2的15次方到2的15次方-1)
int 的取值范围:-2147483648~2147483647(-2的31次方到2的31次方-1)
long 的取值范围:-9223372036854774808~9223372036854774807(-2的63次方到2的63次方-1)

Java中允许用_来格式化数字,例如

int num1 = 2018_07_03; // 合法的,主要用于显示上看的更明白
int num2 = 20180703;
int num3 = 99_999_999;// 同理
int num4 = 99999999;

char

char速度比String更快,因此有条件的时候用char比String性能更好:

if(s.startsWith("a"))
// 用charAt()速度较快
if('a' == s.charAt(0))

String a = "abc" + "d";
String b = "abc" + 'd'; // 更好

而且String在内存中有字符串常量池,如果作为密码存储,可以被黑客dump到内存直接获取明文密码,因此作为高可靠性存储密码时通常会选用char[]存储。

浮点型

Java中用二进制数据科学计算法表示浮点数,因此当小数点后数字很多的时候容易丢失精度。除了float和double,可以考虑使用BigDecimal类。

另外浮点型数据还有三个特殊的值:

  • POSITIVE_INFINITY 正无穷大
  • NEGATIVE_INFINITY 负无穷大
  • NaN 非数(表示出错)

用一个正数除以0得到正无穷大,用一个负数除以0得到负无穷大,0.0除以0或对负数开方会得到非数。其中Double.POSITIVE_INFINITY等于Float.POSITIVE_INFINITY;NEGATIVE_INFINITY同理;但是NaN 与任何对象相等,即便另一个对象也是NaN。

因此不能使用x == Double.NaN来检查是否为非数,只能用Double.isNaN(x)来判断;用Double.isInfinite可以测试是否为无穷大;用Double.isFinite检查既不是无穷也不是NaN

这里特别要注意的是 浮点数可以除以0 ,程序不会报任何错误,包括运行时异常; 整形不可以除0 ,程序会报运行时异常。

// Java7开始支持用下划线分割长数字,用来方便查看
int a = 2014_05_31; // 20140531
long b = 3_000_000_000L; // 3000000000
double pi = 3.14_15_92_6; // 3.1415926

如果long类型不够,则使用BigInteger类;如果double类型不够则使用BigDecimal。精确的浮点数计算也需要用到BigDecimal类。

布尔型

Java中boolean类型的值只能是true或false,其他数据类型无法转换成布尔型。

我们要直接用boolean变量判断,而不要用等于判定。如:

boolean flag = true;
if(flag == false) {} // 不要这样使用
if(!flag) {} // 推荐这样使用

这样做的好处一个是能让代码运行更快(生成字节码少5个字节),二个是让代码看起来简洁。

另外

switch可以作用在byte/short/char/int/enum上,也可以作用在String(Java1.7)上,但是不可以作用在long上

String

Java中JVM会维护一个字符串常量池, 通常 情况下创建字符的过程是在池中查找是否已经存在字符内容,如果没有,就创建一个新的并加入到池中,从而节省内存;也有 特殊 情况,即new String(),它会忽略字符串常量池,而重新创建一个字符串。

String.intern()

我们在知道字符串存在常量池的情况下,就能更好的理解这个方法了。intern()是一个本地方法,它返回String池中的对象,如果池中没有该对象,会把其加入到池中再返回。与equals()意义完全不同。

new String()产生的对象不会加入到String池中。

例如:

String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = "a" + "b";
String str5 = "a" + str2;
String str6 = new String("ab");

if(str3 == str6)          // false
// str6是new出来的String,因此它不会复用String池中的对象

if(str3.equale(str6))     // true
// 但是str6内容和str3一样,因此equale方法相等

if(str6.intern() == str3) // true
// str6.intern()返回的是str6在String池中相等的对象,因此就是str3。


if(str3 == str4)          // true
// JVM编译器会优化两个静态字符串拼接
if(str3 == str5)          // false
// 动态字符串拼接会在池中创建新对象
if(str3.equale(str5))     // true
if(str3.intern() == str5) // false
if(str5.intern() == str3) // true
// str5的String池内容就是str3

String.trim()

String.trim()方法可以去掉收尾的空格。但是准确的说,这个方法会去掉首位所有小于等于32的char。之所以大家通常说trim方法去掉空格,是因为在char中,第32位char是空格,而之前的32个char都无法正常显示。

String的subString()内存溢出【仅限于JDK6】

从String源代码中可以看到subString方法的实现是new了一个String对象,里面保存了原始的String的char[]和偏移量。这样做的好处在大多数情况下节省了内存空间,因为new出来的String复用了旧String。但是对于从一个巨大的String中截取少量的String来说,这种方式会导致大量的冗余。

因此当出现需要从巨大String中截取少量String的时候,建议new一个新的字符串用来保存。

```

static class HugeStr {

private String str = new String(new char[10000000]);

public String getSubString(int begin, int end) {
    return str.substring(begin, end);
    // return new String(str.substring(begin, end));
}

}

public static void main(String[] args) {

List list = new ArrayList();

原文  https://xiaozhuanlan.com/topic/4690837215
正文到此结束
Loading...