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

一、Math类

1.源码分析

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

 2)final修饰,不能被继承

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

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

2.常用属性和方法

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

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

 二、Random类

1.源码分析

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

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

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

 3.调用构造器

import java.util.Random;

//Random类
public class Demo02 {
    public static void main(String[] args) {
        //Math类产生随机数
        System.out.println(Math.random());

        //Random类
        //使用带参数的构造器创建对象,参数是long类型
        Random r1 = new Random(100000L);
        int i= r1.nextInt();//生成一个随机数
        System.out.println(i);
        //多次运行发现输出的i都是同一个数,这是因为seed,也就是种子的意思,seed不变,那么产生的随机数也不会变
        //所以我们用System.currentTimeMillis(当前时间距1970-1-1 00:00:00的时间差,long类型)来作为seed
        Random r2 = new Random(System.currentTimeMillis());
        System.out.println(r2.nextInt());

        //使用空参构造器创建对象:表面是在调用无参构造器,其实底层还是调用了有参构造
        //无参构造源码里面  this(seedUniquifier() ^ System.nanoTime());
        //这两者进行异或,由于nanoTime是可变的,所以每次异或出来的值是不同的,所以可以生成不同的随机数
        Random r3 = new Random();
        System.out.println(r3.nextInt());
        //上面是无参的nextInt(),还有一个人带参数的nextInt(int bound),bound为范围
        //这里是10,也就是会生成[0,10)之间的随机数
        System.out.println(r3.nextInt(10));
        //返回[0.0,1.0)之间的double值
        System.out.println(r3.nextDouble());
    }
}

 三、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数组进行赋值

public static void main(String[] args) {
        String str="abc";
        System.out.println(str);
        //空构造器创建对象
        String s1 = new String();
        String k="";
        /*public String() {
              this.value = "".value;
          }
          源码中可以看到this.value就是s1.value,而空.value,可以通过debug上面的k看到,空.value里面没有内容,也就是说
          只是构建了s1这个对象,但是指向的堆是s1:null
         */

        //有参构造
        String s2 = new String("abc");
        /*
        public String(String original) {
            this.value = original.value;
        }
        可以看到,源码中是将传入的original的value作为this.value也就是s2.value
         */

        //有参构造,参数为数组
        String s3 = new String(new char[]{'a','b','c'});
        System.out.println(s3);
        /*
        public String(char value[]) {
            this(value, 0, value.length, null);
        }
         */
}

2.String类常用方法

//常用方法
        //equals():比较两个字符串的值
        String abc1 = new String("abc");
        String abc2 = new String("abc");
        System.out.println(abc1.equals(abc2));  //返回的是true,这里是比较两个字符串的值

        /*
        compareTo()
        会对字符串的每一个字符遍历进行比较。比较的次数是两个字符串里面长度较短的次数。
        例如"abc","abcdef"进行比较,a=a,b=b,c=c,那么比较输出的结果就是"abc".length减去"abcdef".length,也就是输出3-6=-3
        例如"abc","abc"进行比较,都相等,那就是"abc".length减去"abc".length,也就是输出3-3=0
        例如"abc","acc"进行比较,a=a,b≠c,那么就会输出b和c的ASCII的差值,也就是-1。
         */
        System.out.println(abc1.compareTo(abc2));

        //substring():字符串的截取,substring(3):从下标为3(下标从0开始)的开始截取,substring(3,6)截取下标3-5位
        String s6 = new String("abcdefghijk");
        System.out.println(s6.substring(3)); //defghijk
        System.out.println(s6.substring(3,6));  //def

        //concat():字符串拼接
        String s7 = new String("aaaaaaa");
        System.out.println(s6.concat(s7));

        //replace(a,b):将字符串中的a全部替换为b
        String s8 = new String("abcccba");
        System.out.println(s8.replace("a","k"));

        //split("-"):以()内传入的指定的字符串为分隔符,进行分割后组成一个数组
        String s9 = new String("a-b-c-d-f");
        String[] strs = s9.split("-");//Alt+回车,Introduce local variable自动加载,可以看到这里生成的是一个数组
        //对数组进行输出
        System.out.println(Arrays.toString(strs));

        //大小写转换toUpperCase():转大写,toLowerCase转小写
        String abc = new String("ABC");
        System.out.println(abc.toLowerCase());
        System.out.println(abc.toLowerCase().toUpperCase());

        //去除字符串首尾的空格
        String s10 = new String("   a  b   c    ");
        System.out.println(s10.trim());

        //将boolean类型转换为String类型
        System.out.println(String.valueOf(false));
        /*
        public static String valueOf(boolean b) {
        return b ? "true" : "false";
         }
         查看源码可以看到,将传入的参数b和true比较,如果和true相等,则返回true,如果不等于true,则返回false。
         因为boolean值只有true和false。
         */

3.String类内存分析

 编写一段代码如下

public static void main(String[] args) {
        String s1="a"+"b"+"c";
        String s2="ab"+"c";
        String s3="a"+"bc";
        String s4="abc";
        String s5="abc"+"";
    }

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

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

这段代码内存分析如下

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

String s6 = new String("abc");

内存分析如下

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

      String s7 = "abc";
         String s8= s7+"def";
         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,那么数组为 :

调用构造器代码

package com.rzd.commonusedclass;

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

3.可变与不可变

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

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

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

运行结果为true。

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

public class Demo06 {
    public static void main(String[] args) {
        //StringBuilder和StringBuffe用法
        //增
        StringBuilder sb = new StringBuilder("abcdef");
        sb.append("你好世界");
        System.out.println(sb);    //abcdef你好世界

        //删
        sb.delete(6,8);//删除数组下标为[6-8)位置上的字符,一个汉字占1个字符
        System.out.println(sb); //abcdef世界
        sb.deleteCharAt(5);    //删除位置3的字符
        System.out.println(sb);  //abcde世界

        //改-->插入
        sb.insert(2,"梦想。。");
        System.out.println(sb);     //ab梦想。。cde世界
        //改-->替换
        sb.replace(2,3,"理想"); //[2,3) 将“梦”替换为“理想”
        System.out.println(sb); //ab理想想。。cde世界

        //查-->查询单个字符
        System.out.println(sb.charAt(3));
        //查-->查询多个字符
        for (int i = 0; i < sb.length(); i++) {
            System.out.print(sb.charAt(i)+"\t");
        }
        System.out.println();
        //查-->截取
        String str=sb.substring(2,4);
        System.out.println(str);//返回的是一个新的字符串,对StringBuilder没有影响
    }
}

5.StringBuilder和StringBuffer的区别

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

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

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值