第七章 匿名内部类
1. 什么是内部类
在类的内部又定义了一个新的类。被称为内部类。
2. 内部类分类
静态内部类:类似于静态变量
实例内部类:类似于实例变量
局部内部类:类似于局部变量
这里主要讲解一下匿名内部类,其他可参考以下文章
3. 匿名内部类
- 匿名内部类是局部内部类的一种,因为这个类没有名字而得名,叫做匿名内部类。
- 学习匿名内部类主要是让大家以后在阅读别人代码的时候,能够理解。
- 匿名内部类有两个缺点:
缺点1:太复杂,太乱,可读性差。
缺点2:类没有名字,以后想重复使用,不能用。
class Test01{// 静态变量static String country;// 该类在类的内部,所以称为内部类// 由于前面有static,所以称为“静态内部类”static class Inner1{}// 实例变量int age;// 没有static叫做实例内部类。class Inner2{}// 方法public void doSome(){// 局部变量int i = 100;// 局部内部类。class Inner3{}}public void doOther(){// doSome()方法中的局部内部类Inner3,在doOther()中不能用。}// main方法,入口public static void main(String[] args){// 调用MyMath中的mySum方法。MyMath mm = new MyMath();/*Compute c = new ComputeImpl();mm.mySum(c, 100, 200);*///合并(这样写代码,表示这个类名是有的。类名是:ComputeImpl)//mm.mySum(new ComputeImpl(), 100, 200);// 使用匿名内部类,表示这个ComputeImpl这个类没名字了。// 这里表面看上去好像是接口可以直接new了,实际上并不是接口可以new了。// 后面的{} 代表了对接口的实现。// 不建议使用匿名内部类,为什么?// 因为一个类没有名字,没有办法重复使用。另外代码太乱,可读性太差。mm.mySum(new Compute(){public int sum(int a, int b){return a + b;}}, 200, 300);}}// 负责计算的接口interface Compute{// 抽象方法int sum(int a, int b);}// 你自动会在这里编写一个Compute接口的实现类/*class ComputeImpl implements Compute{// 对方法的实现public int sum(int a, int b){return a + b;}}*/// 数学类class MyMath{// 数学求和方法public void mySum(Compute c, int x, int y){int retValue = c.sum(x, y);System.out.println(x + "+" + y + "=" + retValue);}}
第八章 数组
1. 一维数组
1.1 数组的优点和缺点
1、Java语言中的数组是一种引用数据类型。不属于基本数据类型。数组的父类是Object。
2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合。)
3、数组当中可以存储“基本数据类型”的数据,也可以存储“引用数据类型”的数据。
4、数组因为是引用类型,所以数组对象是堆内存当中。(数组是存储在堆当中的)
5、数组当中如果存储的是“java对象”的话,实际上存储的是对象的“引用(内存地址)”,数组中不能直接存储java对象。
6、数组一旦创建,在java中规定,长度不可变。(数组长度不可变)
7、数组的分类:一维数组、二维数组、三维数组、多维数组…(一维数组较多,二维数组偶尔使用!)
8、所有的数组对象都有length属性(java自带的),用来获取数组中元素的个数。
9、java中的数组要求数组中元素的类型统一。比如int类型数组只能存储int类型,Person类型数组只能存储Person类型。
10、数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续。这是数组存储元素的特点(特色)。数组实际上是一种简单的数据结构。
11、所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。(数组中首元素的内存地址作为整个数组对象的内存地址。)
12、数组中每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标是:length - 1下标非常重要,因为我们对数组中元素进行“存取”的时候,都需要通过下标来进行。
13、数组这种数据结构的优点和缺点是什么?
优点:
查询/查找/检索某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。
缺点:
第一:由于要保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候, 效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
第二:数组不能存储大数据量,因为很难在内存空间上找到一块很大的连续的内存空间。
为什么检索效率高? 第一:每一个元素的内存地址在空间存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。数组中存储100个元素,或者存储100万个元素,在元素查询/检索方面,效率是相同的,因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。(算出一个内存地址,直接定位的。)
注意: 对于数组中最后一个元素的增删,是没有效率影响的。
14、怎么声明/定义一个一维数组?
语法格式:int[] array1;
15、怎么初始化一个一维数组呢?
包括两种方式:静态初始化,动态初始化:
静态初始化语法格式:
int[] array = {100, 2100, 300, 55};
动态初始化语法格式:
int[] array = new int[5]; // 这里的5表示数组的元素个数。// 初始化一个5个长度的int类型数组,每个元素默认值0String[] names = new String[6]; // 初始化6个长度的String类型数组,每个元素默认值null。
在这需要注意一个异常:ArrayIndexOutOfBoundsException 什么时候采用静态初始化方式,什么时候使用动态初始化方式呢? 创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式。 创建数组的时候,不确定将来数组中存储哪些数据,可以采用动态初始化,预先分配内存空间。
1.2 main方法上面的“String[] args”
这个方法程序员负责写出来,JVM负责调用。JVM调用的时候一定会传一个String数组过来。
public class ArrayTest05 {public static void main(String[] args) {// JVM默认传递过来的这个数组对象的长度?默认0// 通过测试得出:args不是null。System.out.println("JVM给传递过来的String数组参数,它这个数组的长度是?" + args.length);// 以下这一行代码表示的含义:数组对象创建了,但是数组中没有任何数据。//String[] strs = new String[0];//String[] strs = {}; // 静态初始化数组,里面没东西。//printLength(strs);// 这个数组什么时候里面会有值呢?// 其实这个数组是留给用户的,用户可以在控制台上输入参数,这个参数自动会被转换为“String[] args”// 例如这样运行程序:java ArrayTest05 abc def xyz// 那么这个时候JVM会自动将“abc def xyz”通过空格的方式进行分离,分离完成之后,自动放到“String[] args”数组当中。// 所以main方法上面的String[] args数组主要是用来接收用户输入参数的。// 把abc def xyz 转换成字符串数组:{"abc","def","xyz"}// 遍历数组for (int i = 0; i < args.length; i++) {System.out.println(args[i]);}}public static void printLength(String[] args){System.out.println(args.length); // 0}}
我们使用equals方法时需要考虑一下,引用不可以为空,不然会出现空指针异常
1.3 java中的数组是怎么进行拷贝
public class ArrayTest08 {public static void main(String[] args) {// java中的数组是怎么进行拷贝的呢?//System.arraycopy(5个参数);// 拷贝源(从这个数组中拷贝)int[] src = {1, 11, 22, 3, 4};// 拷贝目标(拷贝到这个目标数组上)int[] dest = new int[20]; // 动态初始化一个长度为20的数组,每一个元素默认值0// 调用JDK System类中的arraycopy方法,来完成数组的拷贝//System.arraycopy(src, 1, dest, 3, 2);// 遍历目标数组/*for (int i = 0; i < dest.length; i++) {System.out.println(dest[i]); // 0 0 0 11 22 ... 0}*/System.arraycopy(src, 0, dest, 0, src.length);for (int i = 0; i < dest.length; i++) {System.out.println(dest[i]);}// 数组中如果存储的元素是引用,可以拷贝吗?当然可以。String[] strs = {"hello", "world!", "study", "java", "oracle", "mysql", "jdbc"};String[] newStrs = new String[20];System.arraycopy(strs, 0, newStrs, 0, strs.length);for (int i = 0; i < newStrs.length; i++) {System.out.println(newStrs[i]);}System.out.println("================================");Object[] objs = {new Object(), new Object(), new Object()};Object[] newObjs = new Object[5];// 思考一下:这里拷贝的时候是拷贝对象,还是拷贝对象的地址。(地址。)System.arraycopy(objs, 0, newObjs, 0, objs.length);for (int i = 0; i < newObjs.length; i++) {System.out.println(newObjs[i]);}}}
拷贝完记得把原数组的引用变为null,便于GC进行垃圾回收
2. 二维数组
二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组
第八章 Arrays工具类
1. 常见排序算法
1.1 冒泡排序
1、每一次循环结束后,都要找出最大的数,放到参与比较的这堆数据的最右边(冒出最大的那个气泡)
2、核心:拿着左边的数字和右边的数字比对,当左边 > 右边的时候,交换位置
3、冒牌排序算法基本代码:
for(int i = arr.length-1; i > 0; i--){for(int j = 0; j < i; j++){// 不管是否需要交换位置,总之是要比较一次的。//count++;if(arr[j] > arr[j+1]){// 交换位置。// arr[j] 和 arr[j+1] 交换int temp;temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;count2++;}}}
1.2 选择排序
- 每一次从这堆“参与比较的数据当中”找出最小值,拿着这个最小值和“参与比较的这堆最前面的元素”交换位置
- 选择排序比冒泡排序好在:每一次的交换位置都是有意义的
- 关键点:选择排序中的关键在于,你怎么找出一堆数据中最小的
public class SelectSort {public static void main(String[] args) {//int[] arr = {3, 1, 6, 2, 5};int[] arr = {9, 8, 10, 7, 6, 0, 11};int count = 0;int count2 = 0;// 选择排序// 5条数据循环4次。(外层循环4次。)for(int i = 0; i < arr.length - 1; i++){// i的值是0 1 2 3// i正好是“参加比较的这堆数据中”最左边那个元素的下标。//System.out.println(i);// i是一个参与比较的这堆数据中的起点下标。// 假设起点i下标位置上的元素是最小的。int min = i;for(int j = i+1; j < arr.length; j++){count++;//System.out.println("===>" + j);if(arr[j] < arr[min]){min = j; //最小值的元素下标是j}}// 当i和min相等时,表示最初猜测是对的。// 当i和min不相等时,表示最初猜测是错的,有比这个元素更小的元素,// 需要拿着这个更小的元素和最左边的元素交换位置。if(min != i){// 表示存在更小的数据// arr[min] 最小的数据// arr[i] 最前面的数据int temp;temp = arr[min];arr[min] = arr[i];arr[i] = temp;count2++;}}// 冒泡排序和选择排序实际上比较的次数没变。// 交换位置的次数减少了。System.out.println("比较次数" + count); // 21System.out.println("交换次数:" + count2); // 5// 排序之后遍历for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}}
2. 数组的元素查找
2.1 二分法查找
- 数组元素查找有两种方式:第一种方式:一个一个挨着找,直到找到为止<br /> 第二种方式:二分法查找(算法),这个效率较高- 二分法查找算法是基于排序的基础之上(没有排序的数据是无法查找的)- 二分法查找的终止条件:一直折半,直到中间的那个元素恰好是被查找的元素
public class ArrayUtil {public static void main(String[] args) {int[] arr = {100,200,230,235,600,1000,2000,9999};// 找出arr这个数组中200所在的下标。// 调用方法int index = binarySearch(arr, 230);System.out.println(index == -1 ? "该元素不存在!" : "该元素下标" + index);}/*** 从数组中查找目标元素的下标。* @param arr 被查找的数组(这个必须是已经排序的。)* @param dest 目标元素* @return -1表示该元素不存在,其它表示返回该元素的下标。*/public static int binarySearch(int[] arr, int dest) {// 开始下标int begin = 0;// 结束下标int end = arr.length - 1;// 开始元素的下标只要在结束元素下标的左边,就有机会继续循环。while(begin <= end) {// 中间元素下标int mid = (begin + end) / 2;if (arr[mid] == dest) {return mid;} else if (arr[mid] < dest) {// 目标在“中间”的右边// 开始元素下标需要发生变化(开始元素的下标需要重新赋值)begin = mid + 1; // 一直增} else {// arr[mid] > dest// 目标在“中间”的左边// 修改结束元素的下标end = mid - 1; // 一直减}}return -1;}}
3. Arrays工具类使用
- binarySearch和sort方法一般是联合使用的,因为二分法查找建立在已经排序的数组
import java.util.Arrays;public class ArraysTest {public static void main(String[] args) {//1.boolean equals(int[] a,int[] b):判断两个数组是否相等。int[] arr1 = new int[]{1,2,3,4};int[] arr2 = new int[]{1,3,2,4};boolean isEquals = Arrays.equals(arr1, arr2);System.out.println(isEquals);//2.String toString(int[] a):输出数组信息。System.out.println(Arrays.toString(arr1));//3.void fill(int[] a,int val):将指定值填充到数组之中。Arrays.fill(arr1,10);System.out.println(Arrays.toString(arr1));//4.void sort(int[] a):对数组进行排序。Arrays.sort(arr2);System.out.println(Arrays.toString(arr2));//5.int binarySearch(int[] a,int key)int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};int index = Arrays.binarySearch(arr3, 210);if(index >= 0){System.out.println(index);}else{System.out.println("未找到");}}}
第九章 String类
1. String类存储原理
- String类在Java.lang包下
- 在java中随便使用双引号括起来的都是String对象。例如:”abc”,”def”,”hello world!”,这是3个String对象。
- java中规定,双引号括起来的字符串,是不可变的,也就是说”abc”自出生到最终死亡,不可变,不能变成”abcd”,也不能变成”ab”
- 在JDK当中双引号括起来的字符串,例如:”abc” “def”都是直接存储在“方法区”的“字符串常量池”当中的。
- 垃圾回收器是不会释放常量的
1.1 String s1 =”abcdef”和String s3 = new String(“xy”)的区别

1.2 String在堆内存的存储

public class StringTest01 {public static void main(String[] args) {// 这两行代码表示底层创建了3个字符串对象,都在字符串常量池当中。String s1 = "abcdef";String s2 = "abcdef" + "xy";// 因为"testString"是一个String字符串对象。只要是对象都能调用方法。String k ="test";System.out.println("testString".equals(k)); // 建议使用这种方式,因为这个可以避免空指针异常。System.out.println(k.equals("testString")); // 存在空指针异常的风险。不建议这样写。}}
2. String类中的构造方法
- 第一个:String s = new String(“”);
- 第二个:String s = “”; 最常用
- 第三个:String s = new String(char数组);
- 第四个:String s = new String(char数组,起始下标,长度);
- 第五个:String s = new String(byte数组);
- 第六个:String s = new String(byte数组,起始下标,长度)
public class StringTest04 {public static void main(String[] args) {// 创建字符串对象最常用的一种方式String s1 = "hello world!";// s1这个变量中保存的是一个内存地址。// 按说以下应该输出一个地址。// 但是输出一个字符串,说明String类已经重写了toString()方法。System.out.println(s1);//hello world!System.out.println(s1.toString()); //hello world!// 这里只掌握常用的构造方法。byte[] bytes = {97, 98, 99}; // 97是a,98是b,99是cString s2 = new String(bytes);// 前面说过:输出一个引用的时候,会自动调用toString()方法,默认Object的话,会自动输出对象的内存地址。// 通过输出结果我们得出一个结论:String类已经重写了toString()方法。// 输出字符串对象的话,输出的不是对象的内存地址,而是字符串本身。System.out.println(s2.toString()); //abcSystem.out.println(s2); //abc// String(字节数组,数组元素下标的起始位置,长度)// 将byte数组中的一部分转换成字符串。String s3 = new String(bytes, 1, 2);System.out.println(s3); // bc// 将char数组全部转换成字符串char[] chars = {'我','是','中','国','人'};String s4 = new String(chars);System.out.println(s4);// 将char数组的一部分转换成字符串String s5 = new String(chars, 2, 3);System.out.println(s5);String s6 = new String("helloworld!");System.out.println(s6); //helloworld!}}
3. String类当中常用方法
public class StringTest05 {public static void main(String[] args) {// String类当中常用方法。//1(掌握).char charAt(int index)char c = "中国人".charAt(1); // "中国人"是一个字符串String对象。只要是对象就能“点.”System.out.println(c); // 国// 2(了解).int compareTo(String anotherString)// 字符串之间比较大小不能直接使用 > < ,需要使用compareTo方法。int result = "abc".compareTo("abc");System.out.println(result); //0(等于0) 前后一致 10 - 10 = 0int result2 = "abcd".compareTo("abce");System.out.println(result2); //-1(小于0) 前小后大 8 - 9 = -1int result3 = "abce".compareTo("abcd");System.out.println(result3); // 1(大于0) 前大后小 9 - 8 = 1// 拿着字符串第一个字母和后面字符串的第一个字母比较,根据排序进行减法。能分胜负就不再比较了。System.out.println("xyz".compareTo("yxz")); // -1// 3(掌握).boolean contains(CharSequence s)// 判断前面的字符串中是否包含后面的子字符串。System.out.println("HelloWorld.java".contains(".java")); // trueSystem.out.println("http://www.baidu.com".contains("https://")); // false// 4(掌握). boolean endsWith(String suffix)// 判断当前字符串是否以某个子字符串结尾。System.out.println("test.txt".endsWith(".java")); // falseSystem.out.println("test.txt".endsWith(".txt")); // trueSystem.out.println("fdsajklfhdkjlsahfjkdsahjklfdss".endsWith("ss")); // true// 5(掌握).boolean equals(Object anObject)// 比较两个字符串必须使用equals方法,不能使用“==”// equals方法有没有调用compareTo方法? 老版本可以看一下。JDK13中并没有调用compareTo()方法。// equals只能看出相等不相等。// compareTo方法可以看出是否相等,并且同时还可以看出谁大谁小。System.out.println("abc".equals("abc")); // true// 6(掌握).boolean equalsIgnoreCase(String anotherString)// 判断两个字符串是否相等,并且同时忽略大小写。System.out.println("ABc".equalsIgnoreCase("abC")); // true// 7(掌握).byte[] getBytes()// 将字符串对象转换成字节数组byte[] bytes = "abcdef".getBytes();for(int i = 0; i < bytes.length; i++){System.out.println(bytes[i]);}// 8(掌握).int indexOf(String str)// 判断某个子字符串在当前字符串中第一次出现处的索引(下标)。System.out.println("oraclejavac++.netc#phppythonjavaoraclec++".indexOf("java")); // 6// 9(掌握).boolean isEmpty()// 判断某个字符串是否为“空字符串”。底层源代码调用的应该是字符串的length()方法。//String s = "";String s = "a";System.out.println(s.isEmpty());// 10(掌握). int length()// 面试题:判断数组长度和判断字符串长度不一样// 判断数组长度是length属性,判断字符串长度是length()方法。System.out.println("abc".length()); // 3System.out.println("".length()); // 0// 11(掌握).int lastIndexOf(String str)// 判断某个子字符串在当前字符串中最后一次出现的索引(下标)System.out.println("oraclejavac++javac#phpjavapython".lastIndexOf("java")); //22// 12(掌握). String replace(CharSequence target, CharSequence replacement)// 替换。// String的父接口就是:CharSequenceString newString = "http://www.baidu.com".replace("http://", "https://");System.out.println(newString); //https://www.baidu.com// 把以下字符串中的“=”替换成“:”String newString2 = "name=zhangsan&password=123&age=20".replace("=", ":");System.out.println(newString2); //name:zhangsan&password:123&age:20// 13(掌握).String[] split(String regex)// 拆分字符串String[] ymd = "1980-10-11".split("-"); //"1980-10-11"以"-"分隔符进行拆分。for(int i = 0; i < ymd.length; i++){System.out.println(ymd[i]);}String param = "name=zhangsan&password=123&age=20";String[] params = param.split("&");for(int i = 0; i <params.length; i++){System.out.println(params[i]);// 可以继续向下拆分,可以通过“=”拆分。}// 14(掌握)、boolean startsWith(String prefix)// 判断某个字符串是否以某个子字符串开始。System.out.println("http://www.baidu.com".startsWith("http")); // trueSystem.out.println("http://www.baidu.com".startsWith("https")); // false// 15(掌握)、 String substring(int beginIndex) 参数是起始下标。// 截取字符串System.out.println("http://www.baidu.com".substring(7)); //www.baidu.com// 16(掌握)、String substring(int beginIndex, int endIndex)// beginIndex起始位置(包括)// endIndex结束位置(不包括)System.out.println("http://www.baidu.com".substring(7, 10)); //www// 17(掌握)、char[] toCharArray()// 将字符串转换成char数组char[] chars = "我是中国人".toCharArray();for(int i = 0; i < chars.length; i++){System.out.println(chars[i]);}// 18(掌握)、String toLowerCase()// 转换为小写。System.out.println("ABCDefKXyz".toLowerCase());// 19(掌握)、String toUpperCase();System.out.println("ABCDefKXyz".toUpperCase());// 20(掌握). String trim();// 去除字符串前后空白System.out.println(" hello world ".trim());// 21(掌握). String中只有一个方法是静态的,不需要new对象// 这个方法叫做valueOf// 作用:将“非字符串”转换成“字符串”//String s1 = String.valueOf(true);//String s1 = String.valueOf(100);//String s1 = String.valueOf(3.14);// 这个静态的valueOf()方法,参数是一个对象的时候,会自动调用该对象的toString()方法吗?String s1 = String.valueOf(new Customer());//System.out.println(s1); // 没有重写toString()方法之前是对象内存地址 com.bjpowernode.javase.string.Customer@10f87f48System.out.println(s1); //我是一个VIP客户!!!!// 我们是不是可以研究一下println()方法的源代码了?System.out.println(100);System.out.println(3.14);System.out.println(true);Object obj = new Object();// 通过源代码可以看出:为什么输出一个引用的时候,会调用toString()方法!!!!// 本质上System.out.println()这个方法在输出任何数据的时候都是先转换成字符串,再输出。System.out.println(obj);System.out.println(new Customer());}}class Customer {// 重写toString()方法@Overridepublic String toString() {return "我是一个VIP客户!!!!";}}
4. StringBuffer
4.1 如果需要进行字符串的频繁拼接:
因为java中的字符串是不可变的,每一次拼接都会产生新字符串。这样会占用大量的方法区内存。造成内存空间的浪费,而StringBuffer这个类便可以解决这个问题
public class StringBufferTest02 {public static void main(String[] args) {// 创建一个初始化容量为16个byte[] 数组。(字符串缓冲区对象)StringBuffer stringBuffer = new StringBuffer();// 拼接字符串,以后拼接字符串统一调用 append()方法。// append是追加的意思。stringBuffer.append("a");stringBuffer.append("b");stringBuffer.append("d");stringBuffer.append(3.14);stringBuffer.append(true);// append方法底层在进行追加的时候,如果byte数组满了,会自动扩容。stringBuffer.append(100L);System.out.println(stringBuffer.toString());// 指定初始化容量的StringBuffer对象(字符串缓冲区对象)StringBuffer sb = new StringBuffer(100);sb.append("hello");sb.append("world");sb.append("hello");sb.append("kitty");System.out.println(sb);}}
4.2 如何优化StringBuffer的性能
在创建StringBuffer的时候尽可能给定一个初始化容量,最好减少底层数组的扩容次数。预估计一下,给一个大一些初始化容量。
关键点:给一个合适的初始化容量。可以提高程序的执行效率。
4.3 String为什么是不可变的
通过源代码发现,String类中有一个byte[]数组,这个byte[]数组采用了final修饰,因为数组一旦创建长度不可变。并且被final修饰的引用一旦指向某个对象之后,不可再指向其它对象,所以String是不可变的!
4.4 StringBuilder/StringBuffer为什么是可变的
通过源代码发现,StringBuffer/StringBuilder内部实际上是一个byte[]数组,这个byte[]数组没有被final修饰,StringBuffer/StringBuilder的初始化容量我记得应该是16,当存满之后会进行扩容,底层调用了数组拷贝的方法System.arraycopy()…是这样扩容的。所以StringBuilder/StringBuffer适合于使用字符串的频繁拼接操作。
4.5 StringBuffer和StringBuilder的区别
- StringBuffer中的方法都有:synchronized关键字修饰。表示StringBuffer在多线程环境下运行是安全的。
- StringBuilder中的方法都没有:synchronized关键字修饰,表示StringBuilder在多线程环境下运行是不安全的。
- StringBuffer是线程安全的。
- StringBuilder是非线程安全的。
第十章 包装类
1. 包装类
1.1 包装类存在的意义
调用某个方法的时候需要传一个数字进去,但数字属于基本数据类型,而该方法参数的类型是Object,这也就引出了包装类
1.2 基本数据类型的包装类
| 基本数据类型 | 包装类型 |
|---|---|
| byte | java.lang.Byte(父类Number) |
| short | java.lang.Short(父类Number) |
| int | java.lang.Integer(父类Number) |
| long | java.lang.Long(父类Number) |
| float | java.lang.Float(父类Number) |
| double | java.lang.Double(父类Number) |
| boolean | java.lang.Boolean(父类Object) |
| char | java.lang.Character(父类Object) |
- 以上八种包装类中,重点以java.lang.Integer为代表进行学习,八种包装类中其中6个都是数字对应的包装类,他们的父类都是Number,可以先研究一下Number中公共的方法:- Number是一个抽象类,无法实例化对象。- Number类中有这样的方法:byte byteValue() 以 byte 形式返回指定的数值。<br /> abstract double doubleValue()以 double 形式返回指定的数值。<br /> abstract float floatValue()以 float 形式返回指定的数值。<br /> abstract int intValue()以 int 形式返回指定的数值。<br /> abstract long longValue()以 long 形式返回指定的数值。<br /> short shortValue()以 short 形式返回指定的数值。<br /> 这些方法其实所有的数字包装类的子类都有,这些方法是负责拆箱的。
1.3 Integer类的构造方法
Integer(int)
Integer(String)
通过访问包装类的常量,来获取最大值和最小值 System.out.println(“int的最大值:” + Integer.MAX_VALUE); System.out.println(“int的最小值:” + Integer.MIN_VALUE); System.out.println(“byte的最大值:” + Byte.MAX_VALUE); System.out.println(“byte的最小值:” + Byte.MIN_VALUE);
1.4 自动装箱和自动拆箱
- 在java5之后,引入了一种新特性,自动装箱和自动拆箱
- 自动装箱:基本数据类型自动转换成包装类- 自动拆箱:包装类自动转换成基本数据类型
public class IntegerTest05 {public static void main(String[] args) {// 900是基本数据类型// x是包装类型// 基本数据类型 --(自动转换)--> 包装类型:自动装箱Integer x = 900;System.out.println(x);// x是包装类型// y是基本数据类型// 包装类型 --(自动转换)--> 基本数据类型:自动拆箱int y = x;System.out.println(y);// z是一个引用,z是一个变量,z还是保存了一个对象的内存地址。Integer z = 1000; // 等同于:Integer z = new Integer(1000);// 分析为什么这个没有报错呢?// +两边要求是基本数据类型的数字,z是包装类,不属于基本数据类型,这里会进行自动拆箱。将z转换成基本数据类型// 在java5之前你这样写肯定编译器报错。System.out.println(z + 1);Integer a = 1000; // Integer a = new Integer(1000); a是个引用,保存内存地址指向对象。Integer b = 1000; // Integer b = new Integer(1000); b是个引用,保存内存地址指向对象。// == 比较的是对象的内存地址,a和b两个引用中保存的对象内存地址不同。// == 这个运算符不会触发自动拆箱机制。(只有+ - * /等运算的时候才会。)System.out.println(a == b); //false}}
1.5 Integer非常重要的面试题
public class IntegerTest06 {public static void main(String[] args) {Integer a = 128;Integer b = 128;System.out.println(a == b); //false/*java中为了提高程序的执行效率,将[-128到127]之间所有的包装对象提前创建好,放到了一个方法区的“整数型常量池”当中了,目的是只要用这个区间的数据不需要再new了,直接从整数型常量池当中取出来。原理:x变量中保存的对象的内存地址和y变量中保存的对象的内存地址是一样的。*/Integer x = 127;Integer y = 127;// == 永远判断的都是两个对象的内存地址是否相同。System.out.println(x == y); //true}}
1.6 NumberFormatException
Integer a = new Integer("123");// 编译的时候没问题,一切符合java语法,运行时会不会出问题呢?// 不是一个“数字”可以包装成Integer吗?不能。运行时出现异常。// java.lang.NumberFormatException//Integer a = new Integer("中文");
1.7 Integer类当中有哪些常用的方法
public class IntegerTest07 {public static void main(String[] args) {// 重点方法// static int parseInt(String s)// 静态方法,传参String,返回int//网页上文本框中输入的100实际上是"100"字符串。后台数据库中要求存储100数字,此时java程序需要将"100"转换成100数字。int retValue = Integer.parseInt("123"); // String -转换-> int//int retValue = Integer.parseInt("中文"); // NumberFormatExceptionSystem.out.println(retValue + 100);// 照葫芦画瓢double retValue2 = Double.parseDouble("3.14");System.out.println(retValue2 + 1); //4.140000000000001(精度问题)float retValue3 = Float.parseFloat("1.0");System.out.println(retValue3 + 1); //2.0// -----------------------------------以下内容作为了解,不需要掌握---------------------------------------// static String toBinaryString(int i)// 静态的:将十进制转换成二进制字符串。String binaryString = Integer.toBinaryString(3);System.out.println(binaryString); //"11" 二进制字符串// static String toHexString(int i)// 静态的:将十进制转换成十六进制字符串。String hexString = Integer.toHexString(16);System.out.println(hexString); // "10"// 十六进制:1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1ahexString = Integer.toHexString(17);System.out.println(hexString); // "11"//static String toOctalString(int i)// 静态的:将十进制转换成八进制字符串。String octalString = Integer.toOctalString(8);System.out.println(octalString); // "10"System.out.println(new Object()); //java.lang.Object@6e8cf4c6// valueOf方法作为了解//static Integer valueOf(int i)// 静态的:int-->IntegerInteger i1 = Integer.valueOf(100);System.out.println(i1);// static Integer valueOf(String s)// 静态的:String-->IntegerInteger i2 = Integer.valueOf("100");System.out.println(i2);}}
1.8 String int Integer之间互相转换

