当前位置:   article > 正文

Java基础学习——Java面向对象(十二)Math类、Random类、String类、StringBuilder类、StringBuffer类_randomstringbuilder

randomstringbuilder

一、Math类

1.源码分析

1)java.lang下的包可以直接使用,无需导包

 2)final修饰,不能被继承

3)构造器是私有的,说明不能创建Math类的对象(不能这么使用:Math math = new Math();)

 4)Math类里面的属性、方法都由static修饰,说明可以通过Math.属性/方法 调用,不需要创建对象

2.常用属性和方法

  1. public class Demo01 {
  2. public static void main(String[] args) {
  3. //常量
  4. System.out.println(Math.PI);
  5. //常用方法
  6. System.out.println("随机数:"+Math.random()); // 取值范围是【0.0,1.0)
  7. System.out.println("绝对值:"+Math.abs(-6));
  8. System.out.println("向上取值(往大取)"+Math.ceil(9.1));
  9. System.out.println("向下取值(往小取)"+Math.floor(9.9));
  10. System.out.println("四舍五入"+Math.round(3.5));
  11. System.out.println("最大值"+Math.max(3,6));
  12. System.out.println("最小值"+Math.min(3,6));
  13. }
  14. }

6)分析源码可以发现,Math.random其实就是在调用Random下的nextDouble方法

 二、Random类

1.源码分析

1)无final修饰,说明是可以创建对象的

无final修饰,说明是可以创建对象的

 2)由无参构造器和有参构造器

 3.调用构造器

  1. import java.util.Random;
  2. //Random类
  3. public class Demo02 {
  4. public static void main(String[] args) {
  5. //Math类产生随机数
  6. System.out.println(Math.random());
  7. //Random类
  8. //使用带参数的构造器创建对象,参数是long类型
  9. Random r1 = new Random(100000L);
  10. int i= r1.nextInt();//生成一个随机数
  11. System.out.println(i);
  12. //多次运行发现输出的i都是同一个数,这是因为seed,也就是种子的意思,seed不变,那么产生的随机数也不会变
  13. //所以我们用System.currentTimeMillis(当前时间距1970-1-1 00:00:00的时间差,long类型)来作为seed
  14. Random r2 = new Random(System.currentTimeMillis());
  15. System.out.println(r2.nextInt());
  16. //使用空参构造器创建对象:表面是在调用无参构造器,其实底层还是调用了有参构造
  17. //无参构造源码里面 this(seedUniquifier() ^ System.nanoTime());
  18. //这两者进行异或,由于nanoTime是可变的,所以每次异或出来的值是不同的,所以可以生成不同的随机数
  19. Random r3 = new Random();
  20. System.out.println(r3.nextInt());
  21. //上面是无参的nextInt(),还有一个人带参数的nextInt(int bound),bound为范围
  22. //这里是10,也就是会生成[0,10)之间的随机数
  23. System.out.println(r3.nextInt(10));
  24. //返回[0.0,1.0)之间的double值
  25. System.out.println(r3.nextDouble());
  26. }
  27. }

 三、String类

1.源码分析

 1)String str="abc";  "abc"是String类的一个实例,也就是类的一个具体的对象

 2)字符串是不可变的(可变字符串后面再学)

3)final修饰,不能被继承

 4)字符串表面看起来是一个字符串,实际上是存放在一个value[]数组里面(JDK1.8之前是char类型数组,1.8之后源码中是byte类型数组)

debug验证:

5)String str="abc";是类似于自动装箱方式创建对象,还可以通过构造器创建对象

构造器的底层源码功能就是给对象底层的value数组进行赋值

  1. public static void main(String[] args) {
  2. String str="abc";
  3. System.out.println(str);
  4. //空构造器创建对象
  5. String s1 = new String();
  6. String k="";
  7. /*public String() {
  8. this.value = "".value;
  9. }
  10. 源码中可以看到this.value就是s1.value,而空.value,可以通过debug上面的k看到,空.value里面没有内容,也就是说
  11. 只是构建了s1这个对象,但是指向的堆是s1:null
  12. */
  13. //有参构造
  14. String s2 = new String("abc");
  15. /*
  16. public String(String original) {
  17. this.value = original.value;
  18. }
  19. 可以看到,源码中是将传入的original的value作为this.value也就是s2.value
  20. */
  21. //有参构造,参数为数组
  22. String s3 = new String(new char[]{'a','b','c'});
  23. System.out.println(s3);
  24. /*
  25. public String(char value[]) {
  26. this(value, 0, value.length, null);
  27. }
  28. */
  29. }

2.String类常用方法

  1. //常用方法
  2. //equals():比较两个字符串的值
  3. String abc1 = new String("abc");
  4. String abc2 = new String("abc");
  5. System.out.println(abc1.equals(abc2)); //返回的是true,这里是比较两个字符串的值
  6. /*
  7. compareTo()
  8. 会对字符串的每一个字符遍历进行比较。比较的次数是两个字符串里面长度较短的次数。
  9. 例如"abc","abcdef"进行比较,a=a,b=b,c=c,那么比较输出的结果就是"abc".length减去"abcdef".length,也就是输出3-6=-3
  10. 例如"abc","abc"进行比较,都相等,那就是"abc".length减去"abc".length,也就是输出3-3=0
  11. 例如"abc","acc"进行比较,a=a,b≠c,那么就会输出b和c的ASCII的差值,也就是-1。
  12. */
  13. System.out.println(abc1.compareTo(abc2));
  14. //substring():字符串的截取,substring(3):从下标为3(下标从0开始)的开始截取,substring(3,6)截取下标3-5位
  15. String s6 = new String("abcdefghijk");
  16. System.out.println(s6.substring(3)); //defghijk
  17. System.out.println(s6.substring(3,6)); //def
  18. //concat():字符串拼接
  19. String s7 = new String("aaaaaaa");
  20. System.out.println(s6.concat(s7));
  21. //replace(a,b):将字符串中的a全部替换为b
  22. String s8 = new String("abcccba");
  23. System.out.println(s8.replace("a","k"));
  24. //split("-"):以()内传入的指定的字符串为分隔符,进行分割后组成一个数组
  25. String s9 = new String("a-b-c-d-f");
  26. String[] strs = s9.split("-");//Alt+回车,Introduce local variable自动加载,可以看到这里生成的是一个数组
  27. //对数组进行输出
  28. System.out.println(Arrays.toString(strs));
  29. //大小写转换toUpperCase():转大写,toLowerCase转小写
  30. String abc = new String("ABC");
  31. System.out.println(abc.toLowerCase());
  32. System.out.println(abc.toLowerCase().toUpperCase());
  33. //去除字符串首尾的空格
  34. String s10 = new String(" a b c ");
  35. System.out.println(s10.trim());
  36. //将boolean类型转换为String类型
  37. System.out.println(String.valueOf(false));
  38. /*
  39. public static String valueOf(boolean b) {
  40. return b ? "true" : "false";
  41. }
  42. 查看源码可以看到,将传入的参数b和true比较,如果和true相等,则返回true,如果不等于true,则返回false。
  43. 因为boolean值只有true和false。
  44. */

3.String类内存分析

 编写一段代码如下

  1. public static void main(String[] args) {
  2. String s1="a"+"b"+"c";
  3. String s2="ab"+"c";
  4. String s3="a"+"bc";
  5. String s4="abc";
  6. String s5="abc"+"";
  7. }

重新编译这段代码,点击IDEA菜单Build-->Recompile 'xxx.java'

反编译这个class文件,这里我使用了一个在线的反编译网站http://www.javadecompilers.com/,结果如下,可以看到,字符串进行了编译器优化,能直接合并成为完整的字符串。

这段代码内存分析如下

 如果用new String来创建一个新的对象

String s6 = new String("abc");

内存分析如下

 如果在创建对象时加入变量

  1.      String s7 = "abc";
  2. String s8= s7+"def";
  3. System.out.println(s8);

由于在编译String s8= s7+"def";  的时候,编译器不会知道s7是“abc”,所以不会像上面的代码一样直接优化成“abcdef”。

四、StringBuilder和StringBuffer类

字符串可以分为两类不可变字符串(String)、可变字符串(StringBuilder、StringBuffer)

1.StringBuilder

1)属性

StringBuilder继承了它的父类的两个属性(JDK1.8之前是char类型数组,1.8之后源码中是byte类型数组)

value[]:StringBuilder底层的存储

 count:value数组中被使用的长度

 2.调用构造器

StringBuilder中有三个构造器

1)空构造器

 可以看到空构造器中是调用了父类的有参构造器,并且传了一个值16

于是跳转到父类构造器

 可以看到源码中的COMPACT_STRINGS初始定义为true,于是走if为真代码,value数组的长度为16。

所以空构造器就是给当前对象的value[]数组的长度赋初始值16。

2)int有参构造器

 可以看到有参构造器也是调用父类的构造器,也就是将我们调用有参构造时传入的数值赋值给value[]数组的长度。

3)string有参构造器

分析后,可以得到:str参数类型的构造器的功能是将value[]数组的长度赋值为str.length+16,

并且将str字符串从下标为0开始放到数组中,将count赋值为数组使用了的长度也就是str的长度

例如,传了一个字符串abc,那么数组为 :

调用构造器代码

  1. package com.rzd.commonusedclass;
  2. public class Demo05 {
  3. public static void main(String[] args) {
  4. //调用StringBuilder空构造器创建StringBuilder对象
  5. StringBuilder sb1 = new StringBuilder();
  6. //调用StringBuilder有参构造器创建StringBuilder对象
  7. StringBuilder sb2 = new StringBuilder(10);
  8. //这时value[]的长度为16+str.length=19,count=3
  9. StringBuilder sb3 = new StringBuilder("abc");
  10. /*如果此时向数组中继续添加字符串,可以使用append方法来添加。
  11. 可以进行多次添加,这里添加了10个a,又添加了10个b
  12. 添加10个a后count为13,数组长度为19,可以放得下,
  13. 再添加10个b后,count数组放不下了,这时底层源码会对数组的长度进行扩容,
  14. 扩容后数组长度为大于19的数(具体查看源码逻辑),扩容后10个b可以添加到数组中,
  15. count为13+10=23。
  16. */
  17. StringBuilder sb=sb3.append("aaaaaaaaaa").append("bbbbbbbbbb");
  18. //输出sb时底层完成了将StringBuilder类型转换为String类型后输出
  19. System.out.println(sb);
  20. }
  21. }

3.可变与不可变

1)String是不可变的,如果给str赋值为“abc”,它在内存中会有固定的地址0x99,当把str修改为“abcdef”时,在内存中会重新开辟新的空间,那么str指向的地址也会改变

2)StringBuilder是可变的,利用append方法追加字符串,地址不会改变,可以利用代码测试

  1. StringBuilder sb4 = new StringBuilder();
  2. System.out.println(sb4.append("abc")==sb4.append("def"));

运行结果为true。

4.StringBuilder和StringBuffer的常用方法(它们的常用方法是一样的)

  1. public class Demo06 {
  2. public static void main(String[] args) {
  3. //StringBuilder和StringBuffe用法
  4. //增
  5. StringBuilder sb = new StringBuilder("abcdef");
  6. sb.append("你好世界");
  7. System.out.println(sb); //abcdef你好世界
  8. //删
  9. sb.delete(6,8);//删除数组下标为[6-8)位置上的字符,一个汉字占1个字符
  10. System.out.println(sb); //abcdef世界
  11. sb.deleteCharAt(5); //删除位置3的字符
  12. System.out.println(sb); //abcde世界
  13. //改-->插入
  14. sb.insert(2,"梦想。。");
  15. System.out.println(sb); //ab梦想。。cde世界
  16. //改-->替换
  17. sb.replace(2,3,"理想"); //[2,3) 将“梦”替换为“理想”
  18. System.out.println(sb); //ab理想想。。cde世界
  19. //查-->查询单个字符
  20. System.out.println(sb.charAt(3));
  21. //查-->查询多个字符
  22. for (int i = 0; i < sb.length(); i++) {
  23. System.out.print(sb.charAt(i)+"\t");
  24. }
  25. System.out.println();
  26. //查-->截取
  27. String str=sb.substring(2,4);
  28. System.out.println(str);//返回的是一个新的字符串,对StringBuilder没有影响
  29. }
  30. }

5.StringBuilder和StringBuffer的区别

String和它们的区别是不可变与可变的。

StringBuilder:JDK1.5开始使用,效率,线程不安全

StringBuffer:JDK1.0开始使用,效率,线程安全

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/106959
推荐阅读
相关标签
  

闽ICP备14008679号