第七章 匿名内部类

1. 什么是内部类

在类的内部又定义了一个新的类。被称为内部类。

2. 内部类分类

静态内部类:类似于静态变量
实例内部类:类似于实例变量
局部内部类:类似于局部变量

这里主要讲解一下匿名内部类,其他可参考以下文章

3. 匿名内部类

  • 匿名内部类是局部内部类的一种,因为这个类没有名字而得名,叫做匿名内部类。
  • 学习匿名内部类主要是让大家以后在阅读别人代码的时候,能够理解。
  • 匿名内部类有两个缺点:

缺点1:太复杂,太乱,可读性差。
缺点2:类没有名字,以后想重复使用,不能用。

  1. class Test01{
  2. // 静态变量
  3. static String country;
  4. // 该类在类的内部,所以称为内部类
  5. // 由于前面有static,所以称为“静态内部类”
  6. static class Inner1{
  7. }
  8. // 实例变量
  9. int age;
  10. // 没有static叫做实例内部类。
  11. class Inner2{
  12. }
  13. // 方法
  14. public void doSome(){
  15. // 局部变量
  16. int i = 100;
  17. // 局部内部类。
  18. class Inner3{
  19. }
  20. }
  21. public void doOther(){
  22. // doSome()方法中的局部内部类Inner3,在doOther()中不能用。
  23. }
  24. // main方法,入口
  25. public static void main(String[] args){
  26. // 调用MyMath中的mySum方法。
  27. MyMath mm = new MyMath();
  28. /*
  29. Compute c = new ComputeImpl();
  30. mm.mySum(c, 100, 200);
  31. */
  32. //合并(这样写代码,表示这个类名是有的。类名是:ComputeImpl)
  33. //mm.mySum(new ComputeImpl(), 100, 200);
  34. // 使用匿名内部类,表示这个ComputeImpl这个类没名字了。
  35. // 这里表面看上去好像是接口可以直接new了,实际上并不是接口可以new了。
  36. // 后面的{} 代表了对接口的实现。
  37. // 不建议使用匿名内部类,为什么?
  38. // 因为一个类没有名字,没有办法重复使用。另外代码太乱,可读性太差。
  39. mm.mySum(new Compute(){
  40. public int sum(int a, int b){
  41. return a + b;
  42. }
  43. }, 200, 300);
  44. }
  45. }
  46. // 负责计算的接口
  47. interface Compute{
  48. // 抽象方法
  49. int sum(int a, int b);
  50. }
  51. // 你自动会在这里编写一个Compute接口的实现类
  52. /*
  53. class ComputeImpl implements Compute{
  54. // 对方法的实现
  55. public int sum(int a, int b){
  56. return a + b;
  57. }
  58. }
  59. */
  60. // 数学类
  61. class MyMath{
  62. // 数学求和方法
  63. public void mySum(Compute c, int x, int y){
  64. int retValue = c.sum(x, y);
  65. System.out.println(x + "+" + y + "=" + retValue);
  66. }
  67. }

第八章 数组

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};
动态初始化语法格式:

  1. int[] array = new int[5]; // 这里的5表示数组的元素个数。
  2. // 初始化一个5个长度的int类型数组,每个元素默认值0
  3. String[] names = new String[6]; // 初始化6个长度的String类型数组,每个元素默认值null。

在这需要注意一个异常:ArrayIndexOutOfBoundsException 什么时候采用静态初始化方式,什么时候使用动态初始化方式呢? 创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式。 创建数组的时候,不确定将来数组中存储哪些数据,可以采用动态初始化,预先分配内存空间。

1.2 main方法上面的“String[] args”

这个方法程序员负责写出来,JVM负责调用。JVM调用的时候一定会传一个String数组过来。

  1. public class ArrayTest05 {
  2. public static void main(String[] args) {
  3. // JVM默认传递过来的这个数组对象的长度?默认0
  4. // 通过测试得出:args不是null。
  5. System.out.println("JVM给传递过来的String数组参数,它这个数组的长度是?" + args.length);
  6. // 以下这一行代码表示的含义:数组对象创建了,但是数组中没有任何数据。
  7. //String[] strs = new String[0];
  8. //String[] strs = {}; // 静态初始化数组,里面没东西。
  9. //printLength(strs);
  10. // 这个数组什么时候里面会有值呢?
  11. // 其实这个数组是留给用户的,用户可以在控制台上输入参数,这个参数自动会被转换为“String[] args”
  12. // 例如这样运行程序:java ArrayTest05 abc def xyz
  13. // 那么这个时候JVM会自动将“abc def xyz”通过空格的方式进行分离,分离完成之后,自动放到“String[] args”数组当中。
  14. // 所以main方法上面的String[] args数组主要是用来接收用户输入参数的。
  15. // 把abc def xyz 转换成字符串数组:{"abc","def","xyz"}
  16. // 遍历数组
  17. for (int i = 0; i < args.length; i++) {
  18. System.out.println(args[i]);
  19. }
  20. }
  21. public static void printLength(String[] args){
  22. System.out.println(args.length); // 0
  23. }
  24. }

我们使用equals方法时需要考虑一下,引用不可以为空,不然会出现空指针异常

1.3 java中的数组是怎么进行拷贝

  1. public class ArrayTest08 {
  2. public static void main(String[] args) {
  3. // java中的数组是怎么进行拷贝的呢?
  4. //System.arraycopy(5个参数);
  5. // 拷贝源(从这个数组中拷贝)
  6. int[] src = {1, 11, 22, 3, 4};
  7. // 拷贝目标(拷贝到这个目标数组上)
  8. int[] dest = new int[20]; // 动态初始化一个长度为20的数组,每一个元素默认值0
  9. // 调用JDK System类中的arraycopy方法,来完成数组的拷贝
  10. //System.arraycopy(src, 1, dest, 3, 2);
  11. // 遍历目标数组
  12. /*
  13. for (int i = 0; i < dest.length; i++) {
  14. System.out.println(dest[i]); // 0 0 0 11 22 ... 0
  15. }
  16. */
  17. System.arraycopy(src, 0, dest, 0, src.length);
  18. for (int i = 0; i < dest.length; i++) {
  19. System.out.println(dest[i]);
  20. }
  21. // 数组中如果存储的元素是引用,可以拷贝吗?当然可以。
  22. String[] strs = {"hello", "world!", "study", "java", "oracle", "mysql", "jdbc"};
  23. String[] newStrs = new String[20];
  24. System.arraycopy(strs, 0, newStrs, 0, strs.length);
  25. for (int i = 0; i < newStrs.length; i++) {
  26. System.out.println(newStrs[i]);
  27. }
  28. System.out.println("================================");
  29. Object[] objs = {new Object(), new Object(), new Object()};
  30. Object[] newObjs = new Object[5];
  31. // 思考一下:这里拷贝的时候是拷贝对象,还是拷贝对象的地址。(地址。)
  32. System.arraycopy(objs, 0, newObjs, 0, objs.length);
  33. for (int i = 0; i < newObjs.length; i++) {
  34. System.out.println(newObjs[i]);
  35. }
  36. }
  37. }

拷贝完记得把原数组的引用变为null,便于GC进行垃圾回收

2. 二维数组

二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组

第八章 Arrays工具类

1. 常见排序算法

1.1 冒泡排序

1、每一次循环结束后,都要找出最大的数,放到参与比较的这堆数据的最右边(冒出最大的那个气泡)
2、核心:拿着左边的数字和右边的数字比对,当左边 > 右边的时候,交换位置
3、冒牌排序算法基本代码:

  1. for(int i = arr.length-1; i > 0; i--){
  2. for(int j = 0; j < i; j++){
  3. // 不管是否需要交换位置,总之是要比较一次的。
  4. //count++;
  5. if(arr[j] > arr[j+1]){
  6. // 交换位置。
  7. // arr[j] 和 arr[j+1] 交换
  8. int temp;
  9. temp = arr[j];
  10. arr[j] = arr[j+1];
  11. arr[j+1] = temp;
  12. count2++;
  13. }
  14. }
  15. }

1.2 选择排序

  • 每一次从这堆“参与比较的数据当中”找出最小值,拿着这个最小值和“参与比较的这堆最前面的元素”交换位置
  • 选择排序比冒泡排序好在:每一次的交换位置都是有意义的
  • 关键点:选择排序中的关键在于,你怎么找出一堆数据中最小的
  1. public class SelectSort {
  2. public static void main(String[] args) {
  3. //int[] arr = {3, 1, 6, 2, 5};
  4. int[] arr = {9, 8, 10, 7, 6, 0, 11};
  5. int count = 0;
  6. int count2 = 0;
  7. // 选择排序
  8. // 5条数据循环4次。(外层循环4次。)
  9. for(int i = 0; i < arr.length - 1; i++){
  10. // i的值是0 1 2 3
  11. // i正好是“参加比较的这堆数据中”最左边那个元素的下标。
  12. //System.out.println(i);
  13. // i是一个参与比较的这堆数据中的起点下标。
  14. // 假设起点i下标位置上的元素是最小的。
  15. int min = i;
  16. for(int j = i+1; j < arr.length; j++){
  17. count++;
  18. //System.out.println("===>" + j);
  19. if(arr[j] < arr[min]){
  20. min = j; //最小值的元素下标是j
  21. }
  22. }
  23. // 当i和min相等时,表示最初猜测是对的。
  24. // 当i和min不相等时,表示最初猜测是错的,有比这个元素更小的元素,
  25. // 需要拿着这个更小的元素和最左边的元素交换位置。
  26. if(min != i){
  27. // 表示存在更小的数据
  28. // arr[min] 最小的数据
  29. // arr[i] 最前面的数据
  30. int temp;
  31. temp = arr[min];
  32. arr[min] = arr[i];
  33. arr[i] = temp;
  34. count2++;
  35. }
  36. }
  37. // 冒泡排序和选择排序实际上比较的次数没变。
  38. // 交换位置的次数减少了。
  39. System.out.println("比较次数" + count); // 21
  40. System.out.println("交换次数:" + count2); // 5
  41. // 排序之后遍历
  42. for (int i = 0; i < arr.length; i++) {
  43. System.out.println(arr[i]);
  44. }
  45. }
  46. }

2. 数组的元素查找

2.1 二分法查找

  1. - 数组元素查找有两种方式:
  2. 第一种方式:一个一个挨着找,直到找到为止<br /> 第二种方式:二分法查找(算法),这个效率较高
  3. - 二分法查找算法是基于排序的基础之上(没有排序的数据是无法查找的)
  4. - 二分法查找的终止条件:一直折半,直到中间的那个元素恰好是被查找的元素
  1. public class ArrayUtil {
  2. public static void main(String[] args) {
  3. int[] arr = {100,200,230,235,600,1000,2000,9999};
  4. // 找出arr这个数组中200所在的下标。
  5. // 调用方法
  6. int index = binarySearch(arr, 230);
  7. System.out.println(index == -1 ? "该元素不存在!" : "该元素下标" + index);
  8. }
  9. /**
  10. * 从数组中查找目标元素的下标。
  11. * @param arr 被查找的数组(这个必须是已经排序的。)
  12. * @param dest 目标元素
  13. * @return -1表示该元素不存在,其它表示返回该元素的下标。
  14. */
  15. public static int binarySearch(int[] arr, int dest) {
  16. // 开始下标
  17. int begin = 0;
  18. // 结束下标
  19. int end = arr.length - 1;
  20. // 开始元素的下标只要在结束元素下标的左边,就有机会继续循环。
  21. while(begin <= end) {
  22. // 中间元素下标
  23. int mid = (begin + end) / 2;
  24. if (arr[mid] == dest) {
  25. return mid;
  26. } else if (arr[mid] < dest) {
  27. // 目标在“中间”的右边
  28. // 开始元素下标需要发生变化(开始元素的下标需要重新赋值)
  29. begin = mid + 1; // 一直增
  30. } else {
  31. // arr[mid] > dest
  32. // 目标在“中间”的左边
  33. // 修改结束元素的下标
  34. end = mid - 1; // 一直减
  35. }
  36. }
  37. return -1;
  38. }
  39. }

3. Arrays工具类使用

  • binarySearch和sort方法一般是联合使用的,因为二分法查找建立在已经排序的数组
  1. import java.util.Arrays;
  2. public class ArraysTest {
  3. public static void main(String[] args) {
  4. //1.boolean equals(int[] a,int[] b):判断两个数组是否相等。
  5. int[] arr1 = new int[]{1,2,3,4};
  6. int[] arr2 = new int[]{1,3,2,4};
  7. boolean isEquals = Arrays.equals(arr1, arr2);
  8. System.out.println(isEquals);
  9. //2.String toString(int[] a):输出数组信息。
  10. System.out.println(Arrays.toString(arr1));
  11. //3.void fill(int[] a,int val):将指定值填充到数组之中。
  12. Arrays.fill(arr1,10);
  13. System.out.println(Arrays.toString(arr1));
  14. //4.void sort(int[] a):对数组进行排序。
  15. Arrays.sort(arr2);
  16. System.out.println(Arrays.toString(arr2));
  17. //5.int binarySearch(int[] a,int key)
  18. int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
  19. int index = Arrays.binarySearch(arr3, 210);
  20. if(index >= 0){
  21. System.out.println(index);
  22. }else{
  23. System.out.println("未找到");
  24. }
  25. }
  26. }

第九章 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”)的区别

image.png

1.2 String在堆内存的存储

image.png

  1. public class StringTest01 {
  2. public static void main(String[] args) {
  3. // 这两行代码表示底层创建了3个字符串对象,都在字符串常量池当中。
  4. String s1 = "abcdef";
  5. String s2 = "abcdef" + "xy";
  6. // 因为"testString"是一个String字符串对象。只要是对象都能调用方法。
  7. String k ="test";
  8. System.out.println("testString".equals(k)); // 建议使用这种方式,因为这个可以避免空指针异常。
  9. System.out.println(k.equals("testString")); // 存在空指针异常的风险。不建议这样写。
  10. }
  11. }

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数组,起始下标,长度)
  1. public class StringTest04 {
  2. public static void main(String[] args) {
  3. // 创建字符串对象最常用的一种方式
  4. String s1 = "hello world!";
  5. // s1这个变量中保存的是一个内存地址。
  6. // 按说以下应该输出一个地址。
  7. // 但是输出一个字符串,说明String类已经重写了toString()方法。
  8. System.out.println(s1);//hello world!
  9. System.out.println(s1.toString()); //hello world!
  10. // 这里只掌握常用的构造方法。
  11. byte[] bytes = {97, 98, 99}; // 97是a,98是b,99是c
  12. String s2 = new String(bytes);
  13. // 前面说过:输出一个引用的时候,会自动调用toString()方法,默认Object的话,会自动输出对象的内存地址。
  14. // 通过输出结果我们得出一个结论:String类已经重写了toString()方法。
  15. // 输出字符串对象的话,输出的不是对象的内存地址,而是字符串本身。
  16. System.out.println(s2.toString()); //abc
  17. System.out.println(s2); //abc
  18. // String(字节数组,数组元素下标的起始位置,长度)
  19. // 将byte数组中的一部分转换成字符串。
  20. String s3 = new String(bytes, 1, 2);
  21. System.out.println(s3); // bc
  22. // 将char数组全部转换成字符串
  23. char[] chars = {'我','是','中','国','人'};
  24. String s4 = new String(chars);
  25. System.out.println(s4);
  26. // 将char数组的一部分转换成字符串
  27. String s5 = new String(chars, 2, 3);
  28. System.out.println(s5);
  29. String s6 = new String("helloworld!");
  30. System.out.println(s6); //helloworld!
  31. }
  32. }

3. String类当中常用方法

  1. public class StringTest05 {
  2. public static void main(String[] args) {
  3. // String类当中常用方法。
  4. //1(掌握).char charAt(int index)
  5. char c = "中国人".charAt(1); // "中国人"是一个字符串String对象。只要是对象就能“点.”
  6. System.out.println(c); // 国
  7. // 2(了解).int compareTo(String anotherString)
  8. // 字符串之间比较大小不能直接使用 > < ,需要使用compareTo方法。
  9. int result = "abc".compareTo("abc");
  10. System.out.println(result); //0(等于0) 前后一致 10 - 10 = 0
  11. int result2 = "abcd".compareTo("abce");
  12. System.out.println(result2); //-1(小于0) 前小后大 8 - 9 = -1
  13. int result3 = "abce".compareTo("abcd");
  14. System.out.println(result3); // 1(大于0) 前大后小 9 - 8 = 1
  15. // 拿着字符串第一个字母和后面字符串的第一个字母比较,根据排序进行减法。能分胜负就不再比较了。
  16. System.out.println("xyz".compareTo("yxz")); // -1
  17. // 3(掌握).boolean contains(CharSequence s)
  18. // 判断前面的字符串中是否包含后面的子字符串。
  19. System.out.println("HelloWorld.java".contains(".java")); // true
  20. System.out.println("http://www.baidu.com".contains("https://")); // false
  21. // 4(掌握). boolean endsWith(String suffix)
  22. // 判断当前字符串是否以某个子字符串结尾。
  23. System.out.println("test.txt".endsWith(".java")); // false
  24. System.out.println("test.txt".endsWith(".txt")); // true
  25. System.out.println("fdsajklfhdkjlsahfjkdsahjklfdss".endsWith("ss")); // true
  26. // 5(掌握).boolean equals(Object anObject)
  27. // 比较两个字符串必须使用equals方法,不能使用“==”
  28. // equals方法有没有调用compareTo方法? 老版本可以看一下。JDK13中并没有调用compareTo()方法。
  29. // equals只能看出相等不相等。
  30. // compareTo方法可以看出是否相等,并且同时还可以看出谁大谁小。
  31. System.out.println("abc".equals("abc")); // true
  32. // 6(掌握).boolean equalsIgnoreCase(String anotherString)
  33. // 判断两个字符串是否相等,并且同时忽略大小写。
  34. System.out.println("ABc".equalsIgnoreCase("abC")); // true
  35. // 7(掌握).byte[] getBytes()
  36. // 将字符串对象转换成字节数组
  37. byte[] bytes = "abcdef".getBytes();
  38. for(int i = 0; i < bytes.length; i++){
  39. System.out.println(bytes[i]);
  40. }
  41. // 8(掌握).int indexOf(String str)
  42. // 判断某个子字符串在当前字符串中第一次出现处的索引(下标)。
  43. System.out.println("oraclejavac++.netc#phppythonjavaoraclec++".indexOf("java")); // 6
  44. // 9(掌握).boolean isEmpty()
  45. // 判断某个字符串是否为“空字符串”。底层源代码调用的应该是字符串的length()方法。
  46. //String s = "";
  47. String s = "a";
  48. System.out.println(s.isEmpty());
  49. // 10(掌握). int length()
  50. // 面试题:判断数组长度和判断字符串长度不一样
  51. // 判断数组长度是length属性,判断字符串长度是length()方法。
  52. System.out.println("abc".length()); // 3
  53. System.out.println("".length()); // 0
  54. // 11(掌握).int lastIndexOf(String str)
  55. // 判断某个子字符串在当前字符串中最后一次出现的索引(下标)
  56. System.out.println("oraclejavac++javac#phpjavapython".lastIndexOf("java")); //22
  57. // 12(掌握). String replace(CharSequence target, CharSequence replacement)
  58. // 替换。
  59. // String的父接口就是:CharSequence
  60. String newString = "http://www.baidu.com".replace("http://", "https://");
  61. System.out.println(newString); //https://www.baidu.com
  62. // 把以下字符串中的“=”替换成“:”
  63. String newString2 = "name=zhangsan&password=123&age=20".replace("=", ":");
  64. System.out.println(newString2); //name:zhangsan&password:123&age:20
  65. // 13(掌握).String[] split(String regex)
  66. // 拆分字符串
  67. String[] ymd = "1980-10-11".split("-"); //"1980-10-11"以"-"分隔符进行拆分。
  68. for(int i = 0; i < ymd.length; i++){
  69. System.out.println(ymd[i]);
  70. }
  71. String param = "name=zhangsan&password=123&age=20";
  72. String[] params = param.split("&");
  73. for(int i = 0; i <params.length; i++){
  74. System.out.println(params[i]);
  75. // 可以继续向下拆分,可以通过“=”拆分。
  76. }
  77. // 14(掌握)、boolean startsWith(String prefix)
  78. // 判断某个字符串是否以某个子字符串开始。
  79. System.out.println("http://www.baidu.com".startsWith("http")); // true
  80. System.out.println("http://www.baidu.com".startsWith("https")); // false
  81. // 15(掌握)、 String substring(int beginIndex) 参数是起始下标。
  82. // 截取字符串
  83. System.out.println("http://www.baidu.com".substring(7)); //www.baidu.com
  84. // 16(掌握)、String substring(int beginIndex, int endIndex)
  85. // beginIndex起始位置(包括)
  86. // endIndex结束位置(不包括)
  87. System.out.println("http://www.baidu.com".substring(7, 10)); //www
  88. // 17(掌握)、char[] toCharArray()
  89. // 将字符串转换成char数组
  90. char[] chars = "我是中国人".toCharArray();
  91. for(int i = 0; i < chars.length; i++){
  92. System.out.println(chars[i]);
  93. }
  94. // 18(掌握)、String toLowerCase()
  95. // 转换为小写。
  96. System.out.println("ABCDefKXyz".toLowerCase());
  97. // 19(掌握)、String toUpperCase();
  98. System.out.println("ABCDefKXyz".toUpperCase());
  99. // 20(掌握). String trim();
  100. // 去除字符串前后空白
  101. System.out.println(" hello world ".trim());
  102. // 21(掌握). String中只有一个方法是静态的,不需要new对象
  103. // 这个方法叫做valueOf
  104. // 作用:将“非字符串”转换成“字符串”
  105. //String s1 = String.valueOf(true);
  106. //String s1 = String.valueOf(100);
  107. //String s1 = String.valueOf(3.14);
  108. // 这个静态的valueOf()方法,参数是一个对象的时候,会自动调用该对象的toString()方法吗?
  109. String s1 = String.valueOf(new Customer());
  110. //System.out.println(s1); // 没有重写toString()方法之前是对象内存地址 com.bjpowernode.javase.string.Customer@10f87f48
  111. System.out.println(s1); //我是一个VIP客户!!!!
  112. // 我们是不是可以研究一下println()方法的源代码了?
  113. System.out.println(100);
  114. System.out.println(3.14);
  115. System.out.println(true);
  116. Object obj = new Object();
  117. // 通过源代码可以看出:为什么输出一个引用的时候,会调用toString()方法!!!!
  118. // 本质上System.out.println()这个方法在输出任何数据的时候都是先转换成字符串,再输出。
  119. System.out.println(obj);
  120. System.out.println(new Customer());
  121. }
  122. }
  123. class Customer {
  124. // 重写toString()方法
  125. @Override
  126. public String toString() {
  127. return "我是一个VIP客户!!!!";
  128. }
  129. }

4. StringBuffer

4.1 如果需要进行字符串的频繁拼接:

因为java中的字符串是不可变的,每一次拼接都会产生新字符串。这样会占用大量的方法区内存。造成内存空间的浪费,而StringBuffer这个类便可以解决这个问题

  1. public class StringBufferTest02 {
  2. public static void main(String[] args) {
  3. // 创建一个初始化容量为16个byte[] 数组。(字符串缓冲区对象)
  4. StringBuffer stringBuffer = new StringBuffer();
  5. // 拼接字符串,以后拼接字符串统一调用 append()方法。
  6. // append是追加的意思。
  7. stringBuffer.append("a");
  8. stringBuffer.append("b");
  9. stringBuffer.append("d");
  10. stringBuffer.append(3.14);
  11. stringBuffer.append(true);
  12. // append方法底层在进行追加的时候,如果byte数组满了,会自动扩容。
  13. stringBuffer.append(100L);
  14. System.out.println(stringBuffer.toString());
  15. // 指定初始化容量的StringBuffer对象(字符串缓冲区对象)
  16. StringBuffer sb = new StringBuffer(100);
  17. sb.append("hello");
  18. sb.append("world");
  19. sb.append("hello");
  20. sb.append("kitty");
  21. System.out.println(sb);
  22. }
  23. }

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)
  1. - 以上八种包装类中,重点以java.lang.Integer为代表进行学习,八种包装类中其中6个都是数字对应的包装类,他们的父类都是Number,可以先研究一下Number中公共的方法:
  2. - Number是一个抽象类,无法实例化对象。
  3. - Number类中有这样的方法:
  4. 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)
image.png

通过访问包装类的常量,来获取最大值和最小值 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之后,引入了一种新特性,自动装箱和自动拆箱
    1. - 自动装箱:基本数据类型自动转换成包装类
    2. - 自动拆箱:包装类自动转换成基本数据类型
  1. public class IntegerTest05 {
  2. public static void main(String[] args) {
  3. // 900是基本数据类型
  4. // x是包装类型
  5. // 基本数据类型 --(自动转换)--> 包装类型:自动装箱
  6. Integer x = 900;
  7. System.out.println(x);
  8. // x是包装类型
  9. // y是基本数据类型
  10. // 包装类型 --(自动转换)--> 基本数据类型:自动拆箱
  11. int y = x;
  12. System.out.println(y);
  13. // z是一个引用,z是一个变量,z还是保存了一个对象的内存地址。
  14. Integer z = 1000; // 等同于:Integer z = new Integer(1000);
  15. // 分析为什么这个没有报错呢?
  16. // +两边要求是基本数据类型的数字,z是包装类,不属于基本数据类型,这里会进行自动拆箱。将z转换成基本数据类型
  17. // 在java5之前你这样写肯定编译器报错。
  18. System.out.println(z + 1);
  19. Integer a = 1000; // Integer a = new Integer(1000); a是个引用,保存内存地址指向对象。
  20. Integer b = 1000; // Integer b = new Integer(1000); b是个引用,保存内存地址指向对象。
  21. // == 比较的是对象的内存地址,a和b两个引用中保存的对象内存地址不同。
  22. // == 这个运算符不会触发自动拆箱机制。(只有+ - * /等运算的时候才会。)
  23. System.out.println(a == b); //false
  24. }
  25. }

1.5 Integer非常重要的面试题

  1. public class IntegerTest06 {
  2. public static void main(String[] args) {
  3. Integer a = 128;
  4. Integer b = 128;
  5. System.out.println(a == b); //false
  6. /*
  7. java中为了提高程序的执行效率,将[-128到127]之间所有的包装对象提前创建好,
  8. 放到了一个方法区的“整数型常量池”当中了,目的是只要用这个区间的数据不需要
  9. 再new了,直接从整数型常量池当中取出来。
  10. 原理:x变量中保存的对象的内存地址和y变量中保存的对象的内存地址是一样的。
  11. */
  12. Integer x = 127;
  13. Integer y = 127;
  14. // == 永远判断的都是两个对象的内存地址是否相同。
  15. System.out.println(x == y); //true
  16. }
  17. }

1.6 NumberFormatException

  1. Integer a = new Integer("123");
  2. // 编译的时候没问题,一切符合java语法,运行时会不会出问题呢?
  3. // 不是一个“数字”可以包装成Integer吗?不能。运行时出现异常。
  4. // java.lang.NumberFormatException
  5. //Integer a = new Integer("中文");

1.7 Integer类当中有哪些常用的方法

  1. public class IntegerTest07 {
  2. public static void main(String[] args) {
  3. // 重点方法
  4. // static int parseInt(String s)
  5. // 静态方法,传参String,返回int
  6. //网页上文本框中输入的100实际上是"100"字符串。
  7. 后台数据库中要求存储100数字,此时java程序需要将"100"转换成100数字。
  8. int retValue = Integer.parseInt("123"); // String -转换-> int
  9. //int retValue = Integer.parseInt("中文"); // NumberFormatException
  10. System.out.println(retValue + 100);
  11. // 照葫芦画瓢
  12. double retValue2 = Double.parseDouble("3.14");
  13. System.out.println(retValue2 + 1); //4.140000000000001(精度问题)
  14. float retValue3 = Float.parseFloat("1.0");
  15. System.out.println(retValue3 + 1); //2.0
  16. // -----------------------------------以下内容作为了解,不需要掌握---------------------------------------
  17. // static String toBinaryString(int i)
  18. // 静态的:将十进制转换成二进制字符串。
  19. String binaryString = Integer.toBinaryString(3);
  20. System.out.println(binaryString); //"11" 二进制字符串
  21. // static String toHexString(int i)
  22. // 静态的:将十进制转换成十六进制字符串。
  23. String hexString = Integer.toHexString(16);
  24. System.out.println(hexString); // "10"
  25. // 十六进制: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 1a
  26. hexString = Integer.toHexString(17);
  27. System.out.println(hexString); // "11"
  28. //static String toOctalString(int i)
  29. // 静态的:将十进制转换成八进制字符串。
  30. String octalString = Integer.toOctalString(8);
  31. System.out.println(octalString); // "10"
  32. System.out.println(new Object()); //java.lang.Object@6e8cf4c6
  33. // valueOf方法作为了解
  34. //static Integer valueOf(int i)
  35. // 静态的:int-->Integer
  36. Integer i1 = Integer.valueOf(100);
  37. System.out.println(i1);
  38. // static Integer valueOf(String s)
  39. // 静态的:String-->Integer
  40. Integer i2 = Integer.valueOf("100");
  41. System.out.println(i2);
  42. }
  43. }

1.8 String int Integer之间互相转换

image.png