Java笔记15

Java学习——Java常用类(下)


一、字符串相关

Java中不可变字符串类是String,属于java.lang包, 它也是Java非常重要的类。

我们从API中可以看到:
String类表示字符串。 Java程序中的所有字符串文字(例如"abc" )都实现为此类的实例
字符串是不变的; 它们的值在创建后无法更改字符串缓冲区支持可变字符串因为String对象是不可变的,所以可以共享它们。 例如:

 String str = "abc";

相当于:

 char data[] = {'a', 'b', 'c'};
 String str = new String(data);

理解:在字符串的内部使用一串 char 字符数组去存储,因为数组已经确定长度无法改变,所以字符串一经确定就无法改变。结果正如上述举例。
Java设计了这样的想法,如果两个字符串内容完全相同,则他们俩采用同一内存地址
举例,我们在main方法中如是写道:

public static void main(String[] args) {
    String text1 = "123123";
    String text2 = "123123";
    System.out.println(text1 == text2);
}

因为不是基本数据类型的,== 比较多是两端的内存地址,所以可以验证:
在这里插入图片描述
因为可以共享这个特性,便采用了字符串常量池

(1). 字符串常量池

字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能,JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化

  • 为字符串开辟一个字符串常量池,类似于缓存区
  • 创建字符串常量时,首先坚持字符串常量池是否存在该字符串
  • 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中

实现的基础

  • 实现该优化的基础是因为字符串是不可变的,可以不用担心数据冲突进行共享
  • 运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收

字符串常量池存在于方法区里。方法区就是加载我们代码的内存区。
方法区方(Method Area),又称永久代(Permanent Generation),常称为PermGen,位于非堆空间,又称非堆区(Non-Heapspace)。

在这里插入图片描述

1、方法区
方法区是被所有线程共享,永久代指永久在内存中存储的内容。
所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间。
这些区域存储的是:静态变量+常量+类信息(构造方法/接口定义)+运行时常量池,方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
但是,实例变量存在堆内存中,和方法区无关。
以上,只是逻辑上的定义。在HotSpot中,方法区仅仅只是逻辑上的独立,实际上还是包含在Java堆中,也是就说,方式区在物理上属于Java堆区中的一部分,而永久区(Permanent Generation)就是方法区的实现。

2、堆(heap)

  • 存储的是对象,每个对象都包含一个与之对应的class
  • JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
  • 对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定

堆在逻辑上分为三部分(Perm)(由GC回收的机制决定) :
新生代(Young Generation,常称为YoungGen)
老年代(o1d Generation,常称为o1dGen、TenuringGen)
永久代(Permanent Generation,常称为PermGenI)

2.1、新生区(New/Young Generation)
新生代(Young Generation),常称为YoungGen,位于堆空间;
新生区又分为 Eden区和Survior(幸存区)。
Eden :新创建的对象
survior 0、1:经过垃圾回收,但是垃圾回收次数小于15次的对象

2.2、养老代(o1d Generation)
老年代(o1d Generation),常称为o1dGen,位于堆空间;
old :垃圾回收次数超过15次,依然存活的对象

2.3、永久区(Permanent Generation)
永久代(Permanent Generation),常称为PermGen,位于非堆空间。
永久区是一个常驻内存区域,用于存放JDK自身所携带的class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭VWM才会释放此区域所占用的内存。

3、栈

  • 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)
  • 每个栈中的数据(原始类型和对象引用)都是私有的
  • 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
  • 数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会自动消失

举例:

String text1 = "123123";    //相当于在永久代中创建这样的常量
String text2 = "123123";    //当text2创建时,会在永久代查找是否由相同的常量,有的话就直接使用该内存地址
String text3 = new String("123123");    //而使用new时,指的是在内存空间,新开辟一块内存空间
System.out.println(text1 == text2);
System.out.println(text1 == text3); //相现象比较内存空间都不同,是两个不同对象

结果:
在这里插入图片描述
演变:
2.3.1、方法区的实现的演变:
]dk1.7之前:hotspot虚拟机对方法区的实现为永久代;
]dk1.8及之后:hotspot移除了永久代用元空间(Metaspace),2.3.2、运行时常量池存和字符串常量池的变化

JDK1.7之前:
运行时常量池(包含字符串常量池)存放在方法区,此时 hotspot虚拟机对方法区的实现为永久代。

JDK1.7 :
字符串常量池被从方法区拿到了堆中;
运行时常量池剩下的东西还在方法区,也就是hotspot中的永久代。

JDK1.8 :
hotspot移除了永久代,用元空间(Metaspace)取而代之。这时候,字符串常量池还在堆,
运行时常量池还在方法区,只不过方法区的实现从永久代变成元空间(Metaspace)。

字符串拼接
举例:

String text1 = "123";
String text2 = "456";
String text3 = "789";
text1 = text1+text2+text3;
System.out.println(text1);

简单的字符串拼接,实际情况:
在这里插入图片描述
可以看到拼接过程会产生字符串内存垃圾,但由于存放在永久代中,不能被GC回收。当这个拼接过程很繁大时就会很影响机器性能,我们在Java中会尽量避免使用 + 来拼接字符串 。

使用字符串拼接,我们会使用StringBuffer,StringBuilder,可变字符序列。

(2). StringBuffer和StringBuilder

Java提供了两个可变字符串类 StringBufferStringBuilder ,中文翻译为“字符串缓冲区”。

区别:
StringBuffer是线程安全的,它的方法是支持线程同步,线程同步会操作串行顺序执行,在单线程环境下会影响效率。StringBuilder是StringBuffer单线程版本,Java 5之后发布的,它不是线程安全的, 但它的执行效率很高。

线程同步是一个多线程概念,就是当多个线程访问一个方法时,只能由一个优先级别高的线程先访问,在访问期间会锁定该方法,其他线程只能等到它访问完成释放锁,才能访问。

这两个类,我们仅是用来拼接字符串的。


StringBuffer和StringBuilder具有完全相同的API,即构造方法和普通方法等内容一样。
StringBuilder的中构造方法有4个:

  • StringBuilder():创建字符串内容是空的StringBuilder对象,初始容量默认为16个字符。
  • StringBuilder(CharSequence seq):指定 CharSequence字符串创建StringBuilder对象。
    CharSequence接口类型,它的实现类有:
    String、StringBuffer和StringBuilder等,所以参数seq可以是String、StringBuffer和StringBuilder
    等类型。
  • StringBuilder(int
    capacity):创建字符串内容是空的StringBuilder对象,初始容量由参数capacity指定的。
  • StringBuilder(String str):指定String字符串创建 StringBuilder对象。
    上述构造方法同样适合于StringBuffer类。

我们主要使用的拼接方法:
在这里插入图片描述
举例:

        //线程不安全的实现
        StringBuilder sb = new StringBuilder();
        //线程安全的实现,两个调用方法完全一样,仅是安全不安全的区别
//        StringBuffer sb = new StringBuffer();
        sb.append(123);    //我们更应该使用的字符串拼接方法,更节省内存
        sb.append("good !");
        System.out.println(sb);
    }

结果:
在这里插入图片描述

二、其他Java常用类

比如:Objects、Math、Arrays,BigDecimal等。

(1). Objects

此类包含static实用程序方法,用于操作对象或在操作前检查某些条件。 这些实用程序包括null或null方法,用于计算对象的哈希代码,返回对象的字符串,比较两个对象,以及检查索引或子范围值是否超出范围。
API NOTE:
静态方法如checkIndex(int, int) , checkFromToIndex(int, int, int) ,和checkFromIndexSize(int, int, int)提供用于如果对应于索引和子范围的值超出边界的检查的便利性。 这些静态方法的变体支持定义运行时异常以及相应的异常详细消息,这些消息在值超出范围时抛出。 此类方法接受功能接口参数,即BiFunction实例,它将超出范围的值映射到运行时异常。 将这些方法与作为lambda表达式,方法引用或捕获值的类的参数结合使用时应小心。 在这种情况下,与功能接口分配相关的捕获成本可能超过检查边界的成本。

方法:
在这里插入图片描述
举例:

import java.util.Objects;

public class Demo1 {
    public static void main(String[] args) {
        String s1 = null;
        String s2 = "123";
//        System.out.println(s1.equals(s2));  //当s1为空时,程序直接出错

        //我们在使用外部传参判断时,程序报错停止运行的情况出现会比较糟糕,所以引用工具类Objects
        Person p1 = null;
        Person p2 = new Person();
        System.out.println(Objects.equals(p1,p2));
    }

    //静态内部类,调用比较方便
    public static class Person{
        private String name;
    }
}

结果没有报错:
在这里插入图片描述

  Person p1 = null;
    System.out.println(Objects.isNull(p1) + " " + Objects.nonNull(p1));

结果:
在这里插入图片描述

(2). Math

Java语言是彻底地面向对象语言,进行数学运算也封装到一个类中。这个类是 java.lang.Math,Math类是final的不能被继承。Math 类中包含用于进行基本数学运算的方法,如指数、 对数、平方根和三角函数等。
方法:

舍入方法

  • static double ceil(double a):返回大于或等于a 最小整数。
  • static double floor(double a):返回小于或等于a最大整数。
  • static int round(float a):四舍五入方法。

最大值和最小值

  • static int min(int a, int b):取两个int整数中较小的一个整数。
  • static int min(long a, long b):取两个long整数中较小的一个整数。
  • static int min(float a, float b):取两个float浮点数中较小的一个浮点数。
  • static int min(double a, double b):取两个 double浮点数中较小的一个浮点数。
  • max方法取两个数中较大的一个数,max方法与min方法参数类似也有4个版本,这里不再赘述。

绝对值

  • static int abs(int a):取int整数a的绝对值。
  • static long abs(long a):取long整数a的绝对值。
  • static float abs(float a):取float浮点数a的绝对值。
  • static double abs(double a):取double浮点数a的绝对值。

三角函数

  • static double sin(double a):返回角的三角正弦。

  • static double cos(double a):返回角的三角余弦。

  • static double tan(double a):返回角的三角正切。

  • static double asin(double a):返回一个值的反正弦。

  • static double acos(double a):返回一个值的反余弦。

  • static double atan(double a):返回一个值的反正切。

  • static double toDegrees(double angrad):将弧度转换为角度。

  • static double toRadians(double angdeg):将角度转换为弧度。

对数运算

  • static double log(double a),返回a的自然对数。

平方根

  • static double sqrt(double a),返回a的正平方根。

幂运算

  • static double pow(double a, double b), 返回第一个参数的第二个参数次幂的值。

计算随机值

  • static double random(),返回大于等于 0.0 且小于 1.0随机数。
    常量

  • 圆周率PI。

  • 自然对数的底数E。

举例:

double[] nums = {1.4, 1.5, 1.6};

        // 测试最大值和最小值
        System.out.printf("min(%.1f, %.1f) = %.1f\n", nums[1], nums[2], Math.min(nums[1], nums[2]));
        System.out.printf("max(%.1f, %.1f) = %.1f\n", nums[1], nums[2], Math.max(nums[1], nums[2]));
        System.out.println();

        // 测试三角函数
        // 1π弧度 = 180°
        System.out.printf("toDegrees(0.5π) = %f\n", Math.toDegrees(0.5 * Math.PI));
        System.out.printf("toRadians(180/π) = %f\n", Math.toRadians(180 / Math.PI));
        System.out.println();

        // 测试平方根
        System.out.printf("sqrt(%.1f) = %f\n", nums[2], Math.sqrt(nums[2]));
        System.out.println();

        // 测试幂运算
        System.out.printf("pow(8, 3) = %f\n", Math.pow(8, 3));
        System.out.println();

        // 测试计算随机值
        System.out.printf("0.0~1.0之间的随机数 = %f\n", Math.random());
        System.out.println();

        // 测试舍入
        for (double num : nums) {
            display(num);
        }
    }

    // 测试舍入方法
    public static void display(double n) {
        System.out.printf("ceil(%.1f) = %.1f\n", n, Math.ceil(n));
        System.out.printf("floor(%.1f) = %.1f\n", n, Math.floor(n));
        System.out.printf("round(%.1f) = %d\n", n, Math.round(n));
        System.out.println();
    }

结果:
在这里插入图片描述

三、Arrays

该类包含用于操作数组的各种方法(例如排序和搜索)。 此类还包含一个静态工厂,允许将数组视为列表。
如果指定的数组引用为null,则此类中的方法都抛出NullPointerException ,除非另有说明。

包含在此类的方法中的文件包括的实施方式的简要描述。 这些描述应被视为实施说明 ,而不是规范的一部分 。 只要遵守规范本身,实现者就可以随意替换其他算法。 (例如, sort(Object[])使用的算法不一定是sort(Object[]) ,但它必须是稳定的 。)

方法有很多,详情参考API文档
举例:

        int[] arr = {8,1,2,3,4,5,6,7};
        System.out.println(arr);
        System.out.println(Arrays.toString(arr));
        //排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr)) ;
        //使用二进制搜索算法在指定的字节数组中搜索指定的值,返回该下标。 
        System.out.println(Arrays.binarySearch(arr, 6));
        System.out.println(arr.length);
        //数组复制(动态扩容)
        arr = Arrays.copyOf(arr,15);
        System.out.println(arr. length);

结果:
在这里插入图片描述

三、BiDecimal

BigDecimal类概念
通过在控制台运行0.1+0.2,会发现float和double的运算误差。

public static void main(String[] args) {
    System.out.println(0.1+0.2);
}

结果:
在这里插入图片描述

由于float类型和double类型在运算时可能会有误差,为了实现精确运算则需要借助java.math.BigDecima类加以描述
常用构造方法

public BigDecimal (string va1) {
}

常用方法
下述所有的运算方法,不会影响参与运算的数据本身,运算的结果会被封装为一个新的BigDecima对象,这个对象会通过return返回出去。

  1. public BigDecimal add(BigDecimal augend):加法运算
  2. public BigDecimal subtract(BigDecimal augend) :减法运算
  3. public BigDecimal multiply(BigDecimal augend) :乘法运算
  4. public BigDecimal divide(BigDecimal augend):除法运算

举例:

//封装数据
BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
//进行运算
BigDecimal b3 = b1.add(b2);
System.out.println(b3);

结果:
在这里插入图片描述

总结

Java常用类(下),( ̄︶ ̄)↗ !!!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值