java基础(五)

这次主要记录的是基本数据类型的封装和String的基本使用。

基本数据类型的封装

在java中,八种数据类型也可以封装成对象。所以java相较于C++,面向对象更彻底,而相对于只有面向对象的语言,又有容易操作的面向过程的处理。

基本数据类型 封装数据类型
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

而封装的数据类型,保证了数据的安全性,也为我们提供一些方法,比如把字符串转换成整数,小数之类的,重写的equals()方法来比较是否相等。

基本使用

1
2
3
4
5
6
public static void main(String[] args) {

Integer in = new Integer(10);
System.out.println(in); //打印 10

}

既然是封装,那也就是对象,需要实例化,看样子和平时int a = 10;没啥区别。

1
2
3
4
5
6
7
8
public static void main(String[] args) {

Integer in = new Integer(10);
Integer in2 = new Integer(10);

System.out.println(in == in2); //打印 false
System.out.println(in.equals(in2)); //打印 true
}

还是 == 的注意,只比较基本变量的值是否相等和对象是否相等,对象相等的条件是引用的地址是否同一个。所以这里不出意外, false。
而equals()方法在Object也是比较值或地址是否相等,当在这重写了equals()方法,看下面的代码。

1
2
3
4
5
6
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

下面则是把字符串变为整型各种方法,虽然最后值都是一样。但,运算不一样,parseInt()把它默认当做十进制的1000表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {

String str = "1000";

int in = Integer.parseInt(str);

int in2 = new Integer(str).intValue();

int in3 = new Integer(str);

int in4 = Integer.valueOf(str).intValue();

int in5 = Integer.valueOf(str);

Integer in6 = Integer.parseInt(str,2); //这里打印的是8,1000在2进制就是8
}

但是,如果出现其他字符字母,则运行会报错,下面的代码,我们都知道是11.1,整数为11,但编译器不会把小数点后面的去掉。当然,里面只填11.1,编译器也会直接报错。你可以改为Double类使用浮点数。

1
2
3
4
5
6
public static void main(String[] args) {

Integer in = new Integer("11.1");
System.out.println(in);

}

装箱与拆箱

1
2
3
4
5
6
7
8
public static void main(String[] args) {

Integer in = 1; //装箱,在虚拟机实际运行的是Integer in = new Integer(1);

int in2 = new Integer(1); //拆箱,实际运行的是int in2 = 1;

//箱,听起来的感觉就是包装成对象,封装成一个对象,拆解为普通的变量
}

装箱与拆箱也改变了一点变化。我们来看看。

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {

Integer in = 10;
Integer in2 = 10;

Integer in3 = 130;
Integer in4 = 130;

System.out.println(in == in2);
System.out.println(in3 == in4);
}

这两个答案是什么呢?因为是对象,你可以回答都是false。但是,我们先看看它的源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
1
2
3
4
5
6
7
8
9
10
/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}

虽然看着还挺长的,但很多都是注释,注释都是帮助理解的。先看中间的这段代码,看得出来,如果这个数在默认的[low(-128),high(127)]范围外,是直接返回一个数组的值,而这个数组是怎么来的呢?看第一段里从第32行做分水岭,上面操作都在确定数组的范围,下面给cache赋值范围并新建Interger对象赋值。否则在[low,high]范围内呢,返回一个value,而这个value就是它本身。

所以,在装箱中,如果范围在[-128,127]之间,得到的是一个值,不在范围内才是对象。

既然你可以自动装箱和拆箱,那么下面的写法也就是ok的。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {

Integer in = Integer.valueOf("1000");

int in2 = Integer.valueOf("1000");

Integer in3 = Integer.valueOf(1000);

System.out.println("in==in2:"+(in==in2));
System.out.println("in==in3:"+(in==in3));
System.out.println("in.equals(in3):"+in.equals(in3));

}

写东西总是会发现有意思的事,对象和值用 == 是什么比较?没想到是值,以前都没发现,都是对象.equals(对象),值==值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class test01 {
public static void main(String[] args) {

System.out.println( 5 == (new a().t=5) );

a a = new a();
a.t = 10;

System.out.println(10 == a.t);

}
}

class a
{
int t;
}

打印的两个都是true。

以上是关于Int型的封装Integer的使用,八种基本数据类型,封装都差不多,但也还是有不同,可以试试其他,比如integer的加法比较,Double的大小比较,Boolean的返回的true和false是不是对象等等。

String

八种基本数据类型之外的都是引用数据类型,比如数组,String,类,接口。而引用数据类型也就意味着它是对象。

1
2
3
public static void main(String[] args) {
String str = "Hello,World";
}

看,String是类,却没有new,很多人初学可能都以为它是特殊的基本数据类型。但它是类,所以它也可以new。

1
2
3
4
5
6
public static void main(String[] args) {
String str = "Hello,World";
String str2 = new String("Hello,World");
System.out.println(str == str2);
System.out.println(str2.equals(str));
}

那第一个打印啥,是false,为啥?在内存中,常量是一开始加载到方法区的常量池,比如我们定义的Final,或字符串都在常量池,而str2是先在常量池找有没有该常量,有的话直接在堆新建一个对象,没有的话,会在常量池存入该常量,接着在堆里新建对象。一个指向常量池,一个指向堆,所以打印false。

1
2
3
4
5
public static void main(String[] args) {
String str = "Hello,World";
String str2 = "Hello,World";
System.out.println(str == str2);
}

这个代码也能说明问题,当我给str赋值时,会去常量池找”Hello,World”这个常量,str2赋值,也会先去常量池找,同时指向同一个地址,那么它们的对象、值也就相等。

String也被称为不可变的字符串,在java中定义如下。

1
public final class String

可是,我们在使用中,往往可以加字符串,但是会产生多个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {

//System.identityHashCode(Object) 打印在内存中的地址

String str; //一开始str为null,没有对象,也就没有哈希地址

str = "Hello,World"; //把常量池的"Hello,World"的地址给了str
System.out.println(System.identityHashCode("Hello,World")); //2018699554
System.out.println(System.identityHashCode(str)); //两个地址在我的电脑上都是上面这一串

str += "! java"; //接着给str又赋值
System.out.println(System.identityHashCode(str)); //1311053135 这是新的str的地址
System.out.println(System.identityHashCode("! java")); //118352462 这是该字符串的地址
}

这里新建了一次str对象。而对象呢,也就创建了一个,因为”Hello,World”和”! java”一开始就加载到了常量池,使用的时候直接指向在常量池的对象就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {

//System.identityHashCode(Object) 打印在内存中的地址

String str = new String("Hello");
System.out.println(System.identityHashCode(str)); //2018699554
System.out.println(System.identityHashCode("Hello")); //1311053135

String str2 = new String("Hello");
System.out.println(System.identityHashCode(str2)); //118352462
System.out.println(System.identityHashCode("Hello")); //1311053135

String str3 = "He" + "llo";
System.out.println(System.identityHashCode(str3)); //1311053135
System.out.println(System.identityHashCode("He")); //1550089733
System.out.println(System.identityHashCode("llo")); //865113938

String str = str+str;
System.out.println(System.identityHashCode(str)); //1442407170
System.out.println(System.identityHashCode("HelloHello")); //1028566121
}

每次地址都不一样,也就说明对象也不一样,一开始str新建String对象,而常量池没有”Hello”,于是先在常量池新建”Hello”对象,接着给str新建对象,所以第5行代码新建了两个对象,我们也看到str和”Hello”地址不一样。
而str2也新建”Hello”对象,但是常量池有”Hello”的对象,所以直接在堆里新建一个对象,所以这里只新建str2对象。看得出”Hello”地址没变。

而str3是把”He”和”llo”合在一起,构成一个字符串,去常量池找,所以直接指向常量池的对象地址。没有创建对象。

最后str把str+str,因为字符串不可变,而常量池没有”HelloHello”,所以先在常量池创建新对象”HelloHello”,str也更改对象,获取“helloHello”。创建了两个对象。

所以上面的代码最少创建5个对象,

1
2
3
4
5
6
public static void main(String[] args) {

String str = String.valueOf(1.21);
System.out.println(str);

}

String自然也可以用装箱和拆箱使用一些方法。

String常用方法

方法 解释
isEmpty() 是否为空,如果空返回true,否则返回flag
charAt(int) 返回字符串下标为int的值
length() 返回字符串的长度,从0开始计算,但字符串末尾默认以\0结束
indexOf(String||char) 返回字符或子字符串的最开的出现的下标
lastIndexOf(String||char) 返回最后字符或子字符串的出现的下标
startsWith(String) 是否以当前字符串开头
endsWith(String) 是否以当前字符串结尾
replace(String||char,String||char) 替换字符
toUpperCase() 字符串全变为大写字母
toLowerCase() 字符串全变为小写字母
split(String) 切割字符串
toCharArray() 变为字符数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
	public static void main(String[] args) {

String str = "";
System.out.println("str是否为空:" + str.isEmpty()); //str是否为空:true

str = "Hello,World";
System.out.println("str赋值后是否为空:" + str.isEmpty()); //str赋值后是否为空:false

System.out.println("str的下标第四个的值:"+ str.charAt(4)); //str的下标第四个的值:o

System.out.println("str的长度为多少:" + str.length()); //str的长度为多少:11

System.out.println("返回l最开始出现的的下标:" + str.indexOf('l')); //返回l最开始出现的的下标:2

System.out.println("返回从下标4开始,l最开始出现的下标:" + str.indexOf('l',4)); //返回从下标4开始,l最开始出现的下标:9

System.out.println("返回l最后出现的下标" + str.lastIndexOf('l')); //返回l最后出现的下标9

System.out.println("str是否以hello开头:" + str.startsWith("Hello")); //str是否以hello开头:true

System.out.println("str是否以hello结尾:" + str.endsWith("Hello")); //str是否以hello结尾:false

System.out.println("把str里的,变为空格" + str.replace(",", " ")); //把str里的,变为空格Hello World

System.out.println("str全部转换成大写字母" + str.toUpperCase()); //str全部转换成大写字母HELLO,WORLD

System.out.println("str全部转换成小写字母" + str.toLowerCase()); //str全部转换成小写字母hello,world


//以","把字符串分割
String[] str2 = str.split(",");
for(String s:str2)System.out.println(s);

/*
* Hello
* World
*/

//转换成字符数组
char[] str3 = str.toCharArray();
for(char s:str3) System.out.println(s);

/*
* H
* e
* l
* l
* o
* ,
* W
* o
* r
* l
* d
*/

}

当然,String方法还有重写的equals()、hashCode()等等方法….

谢谢您对我的支持
0%