文章目录
字符串String
接口:CharSequence
关系:
CharSequence 是一个接口,String类是一个它的实现类,AbstractStringBuilder 抽象类也是一个它的实现类,StringBuffer和StringBuilder 全都继承了 AbstractStringBuilder 抽象类,实现了 CharSequence 接口的方法。
接口规范的抽象方法:
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
实现类实现方法:
- String
- StringBuffer
- StringBuilder
char 和 String 的关系
声明方式
字面量声明
- 概念:用双引号引住的字符串叫做字面量
- 常用
字符串对象声明
- 使用new运算符声明的字符串叫做字符串对象
- 不常用
三种不同方式【jdk1.8】
String str = "AB";
:声明时先去常量池看有没有,有直接指向,没有则在堆中新开辟一个字符数组,在常量池新开辟并存堆中字符数组首地址和哈希值 ,并指向堆中存值的数组首地址,然后 str 指向常量池的哈希键值对String str = new String("AB");
:声明时先去常量池看有没有,有直接指向,没有则在堆中新开辟一个字符数组,并开辟一个键值对空间存放哈希和首地址 ,并指向堆中存值的数组首地址,str指向哈希键值对,然后把哈希键值对复制一份放到常量池中Sring str = new String(变量);
:在堆中新开辟一个字符数组,并开辟一个键值对空间存放哈希和首地址 ,并指向堆中存值的数组首地址,用str.intern
则把 哈希键值对复制一份放到常量池中,不用则不会- 常量池中相同值的哈希优先级最高的是第一种
说明:
- 引号声明的字符串,直接在字符串常量池中生成【生成是指把字符串存储数组的哈希值和首地址存上,真实值是在堆中的】【jdk7把String Pool放到Heap中,jdk8取消了Perm区,新增了元数据区】
- String Pool 其实就是一个Hash Table【默认1009, jdk7可以改变这个值】
- String Pool中实际存储的是堆中存储具体字符串值的数组【char数组存的,1.9之后变成byte数组存】的首地址和一个哈希值
- new 出来的String对象是放在Heap堆里面的
代码示例:
package com.bigdata.practice0924;
public class StringTest {
public static void main(String[] args) {
// 字面量声明字符串 直接把str指向常量池里面存的【1.8】 gc 这个字符数组在堆中存值的首地址和哈希值组成的键值对在字符串常量池中的首地址
String str = "gc";
String str1 = "gc";
String str2 = str1;
String s1 = "g";
// new的方式声明字符串变量
String str3 = new String("gc");
String str4 = new String("gc");
String str5 = new String(str);
String s2 = new String("c");
char[] chs = {'a','b'}; // 堆里面的数组
String str6 = new String(chs); // chs是一个字符数组变量 把这个变量的首地址给到一个String类型的变量str6【即str6指向chs在堆中的首地址】
str6.hashCode();
// System.out.println();
// 手动放实参声明的String变量到常量池 它不会自己去放
String str7 = str6.intern();// str6.intern() 是将该字符串的首地址放到常量池中【常量池分配键值对空间存哈希和该str6在堆中的首地址】 然后把str7指向该哈希键值对的首地址
str7.hashCode();
// System.out.println();
String str8 = "ab";// 声明好后先去常量池找看有没有这个值 有则指向对应地址 没有再去堆开辟 然后常量池开辟键值对存哈希和开辟的首地址 然后该变量指向常量池刚开的哈希键值对首地址
str8.hashCode();
// System.out.println();
System.out.println(str6 == str7); // false
System.out.println(str6 == str8); // false
System.out.println(str7 == str8); // true
// System.out.println(str.hashCode());
// System.out.println(str3.hashCode());
// System.out.println(str5.hashCode());
// System.out.println(s1.hashCode());
// System.out.println(s2.hashCode());
}
}
常用API
常用方法表
示例代码:
package com.bigdata.practice0924;
import java.util.Iterator;
public class StringTest1 {
public static void main(String[] args) {
String str1 = "李";
String str2 = "kui";
String str3 = new String(str2);
String str4 = "KuI";
// 字符串以char数组形式保存 每一位存放一个字符
// 比较 equals(String) 重写的object的 比较里面存放的内容 equalsIgnoreCase(String) 忽略大小写去比较
System.out.println(str2.equals(str3));
System.out.println(str2.equalsIgnoreCase(str4));
// 字符串长度获取 str.length() 从1开始
System.out.println(str2.length());
// str.charAt(int) 获取字符串str中的每一个字符
System.out.println(str2.charAt(2));
// str.indexOf(String) 获取str中第一次出现String的位置 也可以是char
System.out.println(str2.indexOf("k"));
// lastIndexOf(String) 判断传入字符串在字符串中最后一次出现的位置 也可以是char
System.out.println(str2.lastIndexOf('u'));
// startsWith(String) endsWith(String) 判断原字符串是否以传入字符串开头或者结尾
System.out.println(str1.startsWith(""));
// compareTo(String)参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值,
System.out.println("abc".compareTo("abd"));
// 如果两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值 this.length() - String.length() 有正负
System.out.println("HAiac".compareTo("HAi"));
// 不考虑大小写,按字典顺序比较两个字符串 此方法返回一个整数,它的正负号是调用 compareTo 的正负号
System.out.println(str2.compareToIgnoreCase(str4));
// toLowerCase() toUpperCase() 获取大写或者小写的字符串,全部大写或小写返回
System.out.println(str4.toLowerCase());
// substring(int) 从传入参数开始截取到最后 substring(int, int) 截取区间
System.out.println("abcd".substring(2)); // cd
System.out.println("abcd".substring(1,2)); // b
// trim() 去掉字符串首尾的空格
System.out.println(" ac d ".trim()); // ac d
// split(String) 分隔原有字符串 按传入参数 成字符串数组 String[]
String[] strs = "abc def".split(" "); // abc def 两个元素的字符串数组
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
// replace(String, String) 将字符串里面的指定内容替换为别的
System.out.println("cbadd".replace('d', 'e')); // cbaee
System.out.println("cbadd".replace("dd", "e")); // cbae
// 转义字符 \\ --》 \ ,\t --》 制表4 ,\n 换行
System.out.print("\\" + "\t" + "b" + "\n" + "d");
// format()方法 格式化字符串 静态的
System.out.println();
System.out.println(String.format("%s,%d,%f,%b,%c", "abc", 1, 9.99, true, 'c')); // 占位符 对应数据类的
}
}
StringBuffer
概述:
动态的,一个字符串类,效率高,线程安全的,
特点:
线程安全的
常用API:
- append()
- insert()
- delete()
- deleteCharAt()
- replace()
- reverse()
- toString()与 toStringCache属性
- length() ,capacity(),trimToSize()
StringBuilder
概述:
动态的,一个字符串类,效率高,非线程安全的
特点:
非线程安全的
常用API:
- append()
- insert()
- delete()
- deleteCharAt()
- replace()
- reverse()
- toString()与 toStringCache属性
- length() ,capacity(),trimToSize()
String、StringBuffer、StringBuilder区别:
- 执行速度:StringBuilder > StringBuffer > String
- 原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的
- 共同之处:
- 都是final类,不允许被继承,主要是从性能和安全性上考虑的,因为这几个类都是经常被使用着,且考虑到防止其中的参数被参数修改影响到其他的应用。
- 它们都实现了 接口
CharSequence
- 它们都是在
java.lang
包下面
- 不同之处:
- 各自特点:
- String:声明不可变对象,每次操作都会生成新的String对象,然后将指针指向新的String对象,所以在经常改变字符串的前提下尽量不使用String。
- StringBuffer:在原有对象的基础上操作,线程安全的,多线程环境推荐使用StringBuffer
- StringBuilder:在原有对象的基础上操作,非线程安全的,性能高于StringBuffer,单线程环境推荐使用StringBuildetr
- StringBuffer与StringBuilder两者共同之处:可以通过append、insert进行字符串的操作,而且它们的API操作大多相同
- String实现了三个接口:Serializable、Comparable、CharSequence
- StringBuilder只实现了两个接口
Serializable、CharSequence
,相比之下String的实例可以通过compareTo方法进行比较,其他两个不可以。 - 如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
- 一个线程访问一个对象中的
synchronized(this)
同步代码块时,其他试图访问该对象的线程将被阻塞
- 各自特点:
- 用法:
- String:适用于少量的字符串操作的情况
- StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
- StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
随机数
java.lang.Math.random()
概述:
random() 是一个静态的Math类方法,返回值是double的随机数,它在 [0, 1) 直接随机返回值
公式:
公式 [min, max) :int n = (int)(Math.random() * (max - min) + min);
四舍五入:
Math.round()
函数是求某个数的整数部分,且四舍五入。
注意 :它是向右取整 -0.5 取 0, -1.5 取 -1; 0.5 取 1, -1.5 取 2
java.util.Random()
概述:
- 是一个类 需要初始化
- 在一个范围里随机抛出来随机数【-2的32到2的32次方-1】
- 有很多方法 nextInt() 重载不同 默认范围出随机数
- 两个构造 无参 有参【long n】—》 n:种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系
- nextInt(x) 这里的x是边界[0,x) 给值<=0 报错AllegalArgumentException
公式:
netInt(max-min+1+min)
代码:
package com.bigdata.practice0924;
import java.util.Random;
public class RandomTest {
public static void main(String[] args) {
// java.lang.Math.random();
// 返回值是double 是一个静态方法 从[0,1) 之间随机返回
double d = Math.random();
System.out.println(d);
// 取[0,10) 之间的整数
int n = (int)(Math.random() * 10);
System.out.println(n);
// 公式 [min, max] :int n = (int)(Math.random() * (max - min) + min);
// [5,13)
System.out.println((int)(Math.random() * 8 + 5));
// Math.round()函数是求某个数的整数部分,且四舍五入。 注意 :它是向右取整 -0.5 取 0, -1.5 取 -1; 0.5 取 1, -1.5 取 2
int s = (int) Math.round(0.988);
System.out.println(s);
System.out.println((int) Math.round(-0.5));
System.out.println((int) Math.round(-1.5));
System.out.println("****************");
System.out.println((int) Math.round(0.5));
System.out.println((int) Math.round(1.5));
// java.util.Random(); 是一个类 需要初始化 在一个范围里随机抛出来的【-2的32到2的32次方-1】
// 有很多方法 nextInt() 重载不同 两个构造 无参 有参【long n】---》 n:种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系
// nextInt(x) 这里的x是边界[0,x) 给值<=0 报错AllegalArgumentException 不写默认范围里面出
Random rand = new Random();
Random rand1 = new Random(15);
System.out.println(rand.nextInt());
System.out.println(rand.nextInt(10));
// 公式 netInt(max-min+1+min) [5,15]
System.out.println(rand.nextInt(16));
System.out.println(rand.nextInt(0));
}
}
随机小红包例子:
package com.bigdata.practice0927;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class RedPackage {
public static void main(String[] args) {
// 发红包:
// 1.输入红包的钱数和发放人数
// 2.随机给每人发放红包 并输出每人得到的金额
Scanner sc = new Scanner(System.in);
Random rd= new Random();
System.out.println("请输入发放红包的钱数:");
Double money = sc.nextDouble();
System.out.println("请输入发放红包的人数:");
int persons = sc.nextInt();
// 格式化红包金额 小数后两位
DecimalFormat df = new DecimalFormat("#.##");
System.out.println("发放红包的钱数和人数分别是:" + df.format(money) + "¥ " + persons + "个人");
System.out.println("开始抢!");
// 存红包
ArrayList<Double> rpli = new ArrayList<Double>();
// 最小红包
Double moneyMin = 0.01;
// 余额
Double balance = money;
// 除最后一次的前几次的总红包钱数 最后一次用传入总的-这个
Double sum = 0.0;
// 如果一个人
if (persons == 1) {
System.out.println("一个人把红包全领了!" + money +"¥" );
return;
}
// 不然
for (int i = 0; i < persons - 1; i++) {
// 随机数[0,1) 后面 * (balance / persons) * 2 处理l一下
String rp = df.format((rd.nextDouble()) * (balance / persons) * 2);
Double rp1 = Double.valueOf(rp);
// // 如果给的值不在范围 则重新给值
// while((rp1 < moneyMin && rp1 > money) && balance == 0) {
// rp = df.format((rd.nextDouble()) * (balance / persons) * 2);
// rp1 = Double.valueOf(rp);
// }
rpli.add(rp1);
balance -= rp1;
}
System.out.println("每个人按抢的顺序对应的红包:");
int i = 0;
for (;i < persons - 1; i++) {
System.out.println("第" + (i+1) + "红包:" + rpli.get(i) + "¥");
sum+=rpli.get(i);
}
// 最后的余额
balance = money - sum;
String rp2 = df.format(balance);
Double rp3 = Double.valueOf(rp2);
System.out.println("第" + (i+1) + "红包:" + rp3 + "¥");
}
}