包装类
包装类的分类
黄色父类都是Object
包装类和基本数据的转换
jdk5 前,是手动装箱和拆箱
jdk5 后,就可以自动装箱和自动拆箱
public class Integer01 {
public static void main(String[] args) {
//演示 int <--> Integer 的装箱和拆箱
//jdk5 前是手动装箱和拆箱
//手动装箱 int->Integer
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱
//Integer -> int
int i = integer.intValue();
//jdk5 后,就可以自动装箱和自动拆箱
int n2 = 200;
//自动装箱 int->Integer
Integer integer2 = n2; //底层使用的是 Integer.valueOf(n2)
//自动拆箱 Integer->int
int n3 = integer2; //底层仍然使用的是 intValue()方法
}
}
包装类型和 String 类型的相互转换 Wrapper VS String
public class WrapperVSString {
public static void main(String[] args) {
//包装类(Integer)->String
Integer i = 100;//自动装箱
//方式 1
String str1 = i + "";
//方式 2
String str2 = i.toString();
//方式 3
String str3 = String.valueOf(i);
//String -> 包装类(Integer)
String str4 = "12345";
Integer i2 = Integer.parseInt(str4);//使用到自动装箱
Integer i3 = new Integer(str4);//构造器
System.out.println("ok~~");
}
}
Integer 类和 Character 类的常用方法
public class WrapperMethod {
public static void main(String[] args) {
System.out.println(Integer.MIN_VALUE); //返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值
System.out.println(Character.isDigit('a'));//判断是不是数字
System.out.println(Character.isLetter('a'));//判断是不是字母
System.out.println(Character.isUpperCase('a'));//判断是不是大写
System.out.println(Character.isLowerCase('a'));//判断是不是小写
System.out.println(Character.isWhitespace('a'));//判断是不是空格
System.out.println(Character.toUpperCase('a'));//转成大写
System.out.println(Character.toLowerCase('A'));//转成小写
}
}
课堂测试题(很重要)
三目运算符是一个整体如果有double,会把精度调为0.0
if else 则不是
Integer 类面试题 1 WrapperExercise02.java
public class WrapperExercise02 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //False
//所以,这里主要是看范围 -128 ~ 127 就是直接返回
/*
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
*/
Integer m = 1; //底层 Integer.valueOf(1); -> 阅读源码
Integer n = 1;//底层 Integer.valueOf(1);
System.out.println(m == n); //T
//所以,这里主要是看范围 -128 ~ 127 就是直接返回
//,否则,就 new Integer(xx);
Integer x = 128;//底层 Integer.valueOf(1);
Integer y = 128;//底层 Integer.valueOf(1);
System.out.println(x == y);//False
}
}
解读:
如果 i 在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回
如果不在 -128~127,就直接 new Integer(i)
public class WrapperExercise03 {
public static void main(String[] args) {
//示例一
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2);//F
//示例二
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);//F
//示例三
Integer i5 = 127;//底层 Integer.valueOf(127)
Integer i6 = 127;//-128~127
System.out.println(i5 == i6); //T
//示例四
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8);//F
//示例五
Integer i9 = 127; //Integer.valueOf(127)
Integer i10 = new Integer(127);
System.out.println(i9 == i10);//F
//示例六
Integer i11=127;
int i12=127;
//只有有基本数据类型,判断的是
//值是否相同
System.out.println(i11==i12); //T
//示例七
Integer i13=128;
int i14=128;
System.out.println(i13==i14);//T
}
}
第一代日期类
Date : 精确到毫秒,代表特定的瞬间
SimpleDateFormat : 格式的解析日期的类
SimpleDateFormat : 格式化的解析日期的具体类
他允许进行格式化(日期 -> 文本),解析(文本 -> 日期)和规范化
Date
获取当前系统时间
这里的 Date 类是在 java.util 包
默认输出的日期格式是国外的方式, 因此通常需要对格式进行转换
Date d1 = new Date(); //获取当前系统时间
System.out.println("当前日期=" + d1);
Date d2 = new Date(9234567); //通过指定毫秒数得到时间
System.out.println("d2=" + d2); //获取某个时间对应的毫秒数
创建 SimpleDateFormat 对象,可以指定相应的格式
这里的格式使用的字母是规定好,不能乱写
format:将日期转换成指定格式的字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 hh:mm:ss E");
String format = sdf.format(d1); // format:将日期转换成指定格式的字符串
System.out.println("当前日期=" + format);
可以把一个格式化的 String 转成对应的 Date
得到 Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
在把 String -> Date , 使用的 sdf 格式需要和你给的 String 的格式一样,否则会抛出转换异常
parse():将指定格式字符串转换成日期
String s = "1996 年 01 月 01 日 10:20:30 星期一";
Date parse = sdf.parse(s);
System.out.println("parse=" + sdf.format(parse));
第二代日期类
calendar(日历)类
Calendar 是一个抽象类, 并且构造器是 private
可以通过 getInstance() 来获取实例
提供大量的方法和字段提供给程序员
Calendar没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活)
如果我们需要按照 24小时进制来获取时间
Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY
Calendar c = Calendar.getInstance(); //创建日历类对象//比较简单,自由
System.out.println("c=" + c);
//2.获取日历对象的某个日历字段
System.out.println("年:" + c.get(Calendar.YEAR));
// 这里为什么要 + 1, 因为Calendar 返回月时候,是按照 0 开始编号
System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:" + c.get(Calendar.HOUR));
System.out.println("分钟:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));
//Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH) +
" " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) );
使用get()方法调用属性
Calendar 返回月时候,是按照 0 开始编号
第三代日期类
LocalDate(日期/年月日),LocalTime(时间/时分秒),LocalDateTime(日期时间/年月日时分秒) JDK8加入
LocalDate只包含日期,可以获取日期字段
LocalTime只包含时间,可以获取时间字段
LocalDateTime包含日期+时间,可以获取日期和时间字段
//1. 使用 now() 返回表示当前日期时间的 对象
LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now();//LocalTime.now()
System.out.println(ldt);
//2. 使用 DateTimeFormatter 对象来进行格式化
// 创建 DateTimeFormatter 对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt);
System.out.println("格式化的日期=" + format);
System.out.println("年=" + ldt.getYear());
System.out.println("月=" + ldt.getMonth());
System.out.println("月=" + ldt.getMonthValue());
System.out.println("日=" + ldt.getDayOfMonth());
System.out.println("时=" + ldt.getHour());
System.out.println("分=" + ldt.getMinute());
System.out.println("秒=" + ldt.getSecond());
LocalDate now = LocalDate.now(); //可以获取年月日
LocalTime now2 = LocalTime.now();//获取到时分秒
//提供 plus 和 minus 方法可以对当前时间进行加或者减
//看看 890 天后,是什么时候 把 年月日-时分秒
LocalDateTime localDateTime = ldt.plusDays(890);
System.out.println("890 天后=" + dateTimeFormatter.format(localDateTime));
//看看在 3456 分钟前是什么时候,把 年月日-时分秒输出
LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
System.out.println("3456 分钟前 日期=" + dateTimeFormatter.format(localDateTime2));
- 使用 now() 返回表示当前日期时间的 对象
LocalDateTime ldt = LocalDateTime.now();
- 使用 DateTimeFormatter 对象来进行格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt);
Instant 时间戳
- 通过 静态方法 now() 获取表示当前时间戳的对象
//1.通过 静态方法 now() 获取表示当前时间戳的对象
Instant now = Instant.now();
System.out.println(now);
- 通过 from 可以把 Instant 转成 Date
//2. 通过 from 可以把 Instant 转成 Date
Date date = Date.from(now);
- 通过 date 的 toInstant() 可以把 date 转成 Instant 对象
//3. 通过 date 的 toInstant() 可以把 date 转成 Instant 对象
Instant instant = date.toInstant();
字符串的特性
- String是一个final类,代表不可变的字符序列
- 字符串是不可变.一个字符串对象一旦被分配,内容是不可变的.
以下语句创建了几个对象:
String s1 = "hello";
s1 = "haha";
两个
以下语句创建了几个对象:
String s1 = "hello"+"abc";
只有一个
编译器会做一个优化,判断创建的常量池对象,是否有引用指向
String a = “hello” + “abc”; —-> String a = “helloabc”;
以下语句创建了几个对象:
String a = "hello";
String b = "abc";
String c = a + b;
底层:
先创建一个
StringBuilder sb = StringBuilder()
执行
sn.append("hello")
现版本:indexCoder = prepend(indexCoder, buf, s2);
执行
sb.append("abc")
现版本:indexCoder = prepend(indexCoder, buf, s1);
执行
String c = sb.toString()
最后是 c 指向堆中的sb对象(String) value[] —> 池中的 “helloabc”
因为toString方法的底层是static String simpleConcat(Object first, Object second) {
String s1 = stringOf(first);
String s2 = stringOf(second);
// start "mixing" in length and coder or arguments, order is not
// important
long indexCoder = mix(initialCoder(), s2);
indexCoder = mix(indexCoder, s1);
byte[] buf = newArray(indexCoder);
// prepend each argument in reverse order, since we prepending
// from the end of the byte array
indexCoder = prepend(indexCoder, buf, s2);
indexCoder = prepend(indexCoder, buf, s1);
return newString(buf, indexCoder);
}
重要规则:如果是两个常量相加,看的是池。String c1 = a + b; 变量相加是在堆中
Arrays
1. toString
返回数组的字符串形式
Arrays.toString(arr)
2. sort排序(自然排序和制定排序)
- 可以直接使用冒泡排序 , 也可以直接使用 Arrays 提供的 sort 方法排序
- 因为数组是引用类型,所以通过 sort 排序后,会直接影响到 实参 arr
- sort 重载的,也可以通过传入一个接口 Comparator 实现定制排序
- 调用 定制排序 时,传入两个参数 (1) 排序的数组 arr (2) 实现了 Comparator 接口的匿名内部类 , 要求实现 compare 方法
- 先演示效果,再解释
- 这里体现了接口编程的方式 , 看看源码,就明白
源码分析:
(1) Arrays.sort(arr, new Comparator()
(2) 最终到 TimSort 类的 private static void binarySort(T[] a, int lo, int hi, int start, Comparator c)()
(3) 执行到 binarySort 方法的代码, 会根据动态绑定机制 c.compare()执行我们传入的 // 匿名内部类的 compare ()
if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
(4)
new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
}
(5) public int compare(Object o1, Object o2) 返回的值>0 还是 <0 // 会影响整个排序结果, 这就充分体现了 接口编程+动态绑定+匿名内部类的综合使用 将来的底层框架和源码的使用方式,会非常常见
Arrays.sort(arr);
// 默认排序方法 //定制排序
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
});
System.out.println("===排序后===");
System.out.println(Arrays.toString(arr));
3.接口+动态排序实现(一天看一遍)
public class ArraysSortCustom {
public static void main(String[] args) {
int[] arr = {1, -1, 8, 0, 20};
//bubble01(arr);
bubble02(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
int i1 = (Integer) o1;
int i2 = (Integer) o2;
return i2 - i1;// return i2 - i1;
}
});
System.out.println("==定制排序后的情况==");
System.out.println(Arrays.toString(arr));
}
//结合冒泡 + 定制
public static void bubble02(int[] arr, Comparator c) {
int temp = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
//数组排序由 c.compare(arr[j], arr[j + 1])返回的值决定
if (c.compare(arr[j], arr[j + 1]) > 0) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
4.binarySearch通过二分搜索法查找,要求必须排好序
int index = Arrays.binarySearch(arr,3);
- 使用 binarySearch 二叉查找
- 要求该数组是有序的. 如果该数组是无序的,不能使用 binarySearch
- 如果数组中不存在该元素,就返回 return -(low + 1); // key not found.
5. copyOf 数组元素的复制
- 从 arr 数组中,拷贝 arr.length 个元素到 newArr 数组中
- 如果拷贝的长度 > arr.length 就在新数组的后面 增加 null
- 如果拷贝长度 < 0 就抛出异常 NegativeArraySizeException
- 该方法的底层使用的是 System.arraycopy()
BigInteger和BigDecimal类
应用场景:
BigInteger适合保存比较大的整形
BigDecimal适合保存精度更高的浮点型(小数)
当我们编程中,需要处理很大的整数,long 不够用 可以使用 BigInteger 的类来搞定BigInteger bigInteger = new BigInteger("23788888899999999999999999999");
BigInteger bigInteger2 = new
BigInteger("10099999999999999999999999999999999999999999999999999999999999999999999999999999999");
System.out.println(bigInteger);
当我们需要保存一个精度很高的数时,double 不够用 可以使用 BigDecimal
double d = 1999.11111111111999999999999977788d;
// System.out.println(d);
BigDecimal bigDecimal = new BigDecimal("1999.11");
BigDecimal bigDecimal2 = new BigDecimal("3");
System.out.println(bigDecimal);
BigInteger运算
- 在对 BigInteger 进行加减乘除的时候,需要使用对应的方法,不能直接进行 + - * /
- 可以创建一个 要操作的 BigInteger 然后进行相应操作
BigInteger add = bigInteger.add(bigInteger2);
System.out.println(add);//加
BigInteger subtract = bigInteger.subtract(bigInteger2);
System.out.println(subtract);//减
BigInteger multiply = bigInteger.multiply(bigInteger2);
System.out.println(multiply);//乘
BigInteger divide = bigInteger.divide(bigInteger2);
System.out.println(divide);//除
BigDecimal运算
- 如果对 BigDecimal 进行运算,比如加减乘除,需要使用对应的方法
- 创建一个需要操作的 BigDecimal 然后调用相应的方法即可
System.out.println(bigDecimal.add(bigDecimal2));
System.out.println(bigDecimal.subtract(bigDecimal2));
System.out.println(bigDecimal.multiply(bigDecimal2));
System.out.println(bigDecimal.divide(bigDecimal2));
//可能抛出异常 ArithmeticException
//在调用 divide 方法时,指定精度即可. BigDecimal.ROUND_CEILING
//如果有无限循环小数,就会保留 分子 的精度
System.out.println(bigDecimal.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
math类
public class MathMethod {
public static void main(String[] args) {
//看看 Math 常用的方法(静态方法)
//1.abs 绝对值
int abs = Math.abs(-9);
System.out.println(abs);//9
//2.pow 求幂
double pow = Math.pow(2, 4);//2 的 4 次方
System.out.println(pow);//16
//3.ceil 向上取整,返回>=该参数的最小整数(转成 double);
double ceil = Math.ceil(3.9);
System.out.println(ceil);//4.0
//4.floor 向下取整,返回<=该参数的最大整数(转成 double)
double floor = Math.floor(4.001);
System.out.println(floor);//4.0
//5.round 四舍五入 Math.floor(该参数+0.5)
long round = Math.round(5.51);
System.out.println(round);//6
//6.sqrt 求开方
double sqrt = Math.sqrt(9.0);
System.out.println(sqrt);//3.0
//7.random 求随机数
// random 返回的是 0 <= x < 1 之间的一个随机小数
// 思考:请写出获取 a-b 之间的一个随机整数,a,b 均为整数 ,比如 a = 2, b=7
// 即返回一个数 x 2 <= x <= 7
// 老韩解读 Math.random() * (b-a) 返回的就是 0 <= 数 <= b-a
// (1) (int)(a) <= x <= (int)(a + Math.random() * (b-a +1) )
// (2) 使用具体的数给小伙伴介绍 a = 2 b = 7
// (int)(a + Math.random() * (b-a +1) ) = (int)( 2 + Math.random()*6)
// Math.random()*6 返回的是 0 <= x < 6 小数
// 2 + Math.random()*6 返回的就是 2<= x < 8 小数
// (int)(2 + Math.random()*6) = 2 <= x <= 7
// (3) 公式就是 (int)(a + Math.random() * (b-a +1) )
for(int i = 0; i < 100; i++) {
System.out.println((int)(2 + Math.random() * (7 - 2 + 1)));
}
//max , min 返回最大值和最小值
int min = Math.min(1, 9);
int max = Math.max(45, 90);
System.out.println("min=" + min);
System.out.println("max=" + max);
}
}
reserve方法
将字符串中指定部分翻转
String str = "abcdef";
System.out.println("===交换前===");
System.out.println(str);
try {
str = reverse(str, 1, 4);
} catch (Exception e) {
System.out.println(e.getMessage());
return;
}
System.out.println("===交换后===");
System.out.println(str);
String VS StringBuffer VS StringBuilder
StringBuilder 和 StringBuffer 非常相似,均代表可变的字符序列,而且方法也一样
String : 不可变字符序列,效率低.但是复用率高
StringBuffer : 可变字符序列,效率较发哦(增删),线程安全,看源码
StringBuilder : 可变字符序列,效率最高,线程不安全
String使用注意说明 :
string s = "a";
创建了一个字符串s += "b";
实际上原来的”a”字符串对象已经丢了,现在又产生了一个字符串s + “b”(也就是”ab”).如果多次执行这些改变串内容的操作,会导致大量副本字符串0对象存留在内存中,降低效率.如果这样的操作放到循环中,会极大影响程序的性能 => 结论:如果字符串存在大量的修改操作,一般使用StringBuffer和StringBuilder.
如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder.
如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer.
如果我们的字符串很少修改,被多个对象引用,使用String,比如配置信息等.
效率: StringBuilder > StringBuffer > String
String类
String 类的理解和创建对象
String 对象用于保存字符串,也就是一组字符序列
“jack” 字符串常量, 双引号括起的字符序列
字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还是汉字)占两个字节
String 类有很多构造器,构造器的重载
常用的有 :
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count)
String s5 = new String(byte[] b)String 类实现了
接口 Serializable【String 可以串行化:可以在网络传输】
接口 Comparable [String 对象可以比较大小]String 是 final 类,不能被其他的类继承
String 有属性 private final char value[]; 用于存放字符串内容
一定要注意:value 是一个 final 类型, 不可以修改(需要功力):即 value 不能指向新的地址,但是单个字符内容是可以变化
String()方法
https://www.runoob.com/java/java-string.html
创建 String 对象的两种方式
String s = "hsp";
String s = new String("hsp");
两种创建 String 对象的区别
方式一:先从常量池查看是否有”hsp”数据空间,如果有,直接指向;如果没有则重新创建,然后指向.s最终指向的是常量池的空间地址
方式二:先从堆中创建空间,里面维护了value属性,指向常量池的hsp空间.如果常量池没有”hsp”,重新创建,如果有,直接通过value指向.最终指向的是堆中的空间地址.
intern() 方法
https://blog.csdn.net/csdn_0911/article/details/79953177
Q:下列程序的输出结果:
String s1 = “abc”;
String s2 = “abc”;
System.out.println(s1 == s2);
A:true
分析一下:程序执行,常量池中生成一个字符串,并将栈中的引用指向 s1,s2 先去查询常量池中是否有已经存在,存在,则返回常量池中的引用,所以s1 = s2。
Q:下列程序的输出结果:
String s1 = new String(“abc”);
String s2 = new String(“abc”);
System.out.println(s1 == s2);
A:false,两个引用指向堆中的不同对象。
分析一下:第一步、常量池中生成一个字符串“abc”,并在堆中生成一个s1引用指向的对象,第二步、先去查询常量池中是否有已经存在“abc”,因为已经存在,所以不在生成,但堆中仍然会生成一个s2引用指向的对象,所以s1 , s2引用不一样。
Q:下列程序的输出结果:
String s1 = “abc”;
String s2 = “a”;
String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);
A:false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。
分析一下:程序执行,s1,s2,s3在常量池中分别生成字符串,并将栈中的引用指向s1,s2,s3,而s4在编译的时候,其方法内部创建StringBuilder来拼接对象,在堆中生成了新的对象。
引号声明的字符串都是会直接在字符串常量池中生成的,而 new 出来的 String 对象是放在堆空间中的。
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
String str3 = str2.intern();
System.out.println(str1==str2);//#1
System.out.print (str1==str3);//#2
}
输出结果为: false true
#1:str1指向常量池中的对象引用,str2指向堆中的对象引用,所以返回false;
#2:str2.intern()指向str1的在常量池中的对象引用,所以str1==str3
public static void main(String[] args) {
String abc = "abc";
final String abcFinal = "abc";
String str1 = "abc01";
String str2 = "abc"+"01";
String str3 = abc + "01";
String str4 = abcFinal+"01";
String str5 = new String("abc01").intern();
System.out.println(str1 == str2);//#3
System.out.println(str1 == str3);//#4
System.out.println(str1 == str4);//#5
System.out.println(str1 == str5);//#6
}
输出的结果为:true false true true
#3:str1指向常量池中的对象引用,而str2在编译期间,+号会自动合并为一个字符串,因为常量池中已经生成了str1的对象,所以str2不在生成,直接将常量池对象的引用赋值给栈中的str2。
#4:str4实际上是使用StringBuilder.append来完成,会生成不同的对象。
#5:final修饰的变量,在编译期间会自动进行常量替换,这是与#4不同。所以不在生成新的对象,直接将常量池对象的引用str1赋值给栈中的str4。
#6:都是指向常量池中的对象引用。
public static void main(String[] args) {
String str2 = new String("abc")+new String("01");
str2.intern();
String str1 = "abc01";
System.out.println(str2==str1);//#7
}
输出的结果为:true
#8:str1指向常量池中的对象引用,而str2指向堆中的对象引用,并在常量池生成俩个对象,“abc”和“01”,接着str2.intern()返回str1指向常量池中的对象引用,但是对于str2没有影响,结果为false,如果是str2 = str2.intern();则最终结果将是true。
当调用 intern 方法是,如果池已经包含一个等于此 String 对象的字符串(用equals(Object)方法确定),则返回池中的字符串.否则,将此String对象添加到池中,并返回此String对象的引用.
b,intern()方法最终返回的是常量池的地址(对象)
format()方法
http://c.biancheng.net/view/4700.html
语法1
format(String format,Object……args)
参数说明:
- format:格式字符串。
- args:格式字符串中由格式说明符引用的参数。参数数目是可变的,可以为 0。
charAt()方法
charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。
public char charAt(int index)
substring() 方法
public String substring(int beginIndex)
或
public String substring(int beginIndex, int endIndex)
StringBuffer类
基本介绍
- StringBuffer代表可变的字符序列,可以对字符内容进行增删.
- 很多方法与String相同,但StringBuffer是可变长度的
- StringBuffer是一个容器
基础
StringBuffer 的直接父类 是 AbstractStringBuilder
StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化
在父类中 AbstractStringBuilder 有属性 char[] value,不是 final
该 value 数组存放 字符串内容不是final的,因此存放在堆中的 (不在常量池)StringBuffer 是一个 final 类,不能被继承
因为 StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除)
String VS StringBuffer
- String保存的是字符串常量,里面的值不能修改,每次String类的更新实际上就是更改地址,效率低(根本原因是final类型的char数组)
private final char value[]
存放在常量池中
- StringBuffer 保存的是字符串变量,里面的值可以更改,每次StringBuffer实际上可以更新内容,不用每次更新地址,效率较高.(空间不够时,才更新地址)
char[] value
存放在堆中
String -> StringBuffer
String str = "hello tom";
方式一 : 使用构造器
注意 : 返回的才是StringBuffer对象,对str本身没有影响
StringBuffer stringbuffer = new StringBuffer(str);
方式二 : 使用的是append方法
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str);
StringBuffer -> String
StringBuffer stringBuffer3 = new StringBuffer("韩顺平教育");
方式 1 : 使用 StringBuffer 提供的 toString 方法
String s = stringBuffer3.toString();
方式 2 : 使用构造器来搞定
String s1 = new String(stringBuffer3);
StringBuffer方法
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer s = new StringBuffer("hello");
//增
s.append(',');// "hello,"
s.append("张三丰");//"hello,张三丰"
s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏 100true10.5" System.out.println(s);//"hello,张三丰赵敏 100true10.5"
//删
/*
* 删除索引为>=start && <end 处的字符
* 解读: 删除 11~14 的字符 [11, 14)
*/
s.delete(11, 14);
System.out.println(s);//"hello,张三丰赵敏 true10.5"
//改
//老韩解读,使用 周芷若 替换 索引 9-11 的字符 [9,11)
s.replace(9, 11, "周芷若");
System.out.println(s);//"hello,张三丰周芷若 true10.5"
//查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6
//插
//老韩解读,在索引为 9 的位置插入 "赵敏",原来索引为 9 的内容自动后移
s.insert(9, "赵敏");
System.out.println(s);//"hello,张三丰赵敏周芷若 true10.5"
//长度
System.out.println(s.length());//22
System.out.println(s);
}
}
练习
public class StringBufferExercise01 {
public static void main(String[] args) {
String str = null;// ok
StringBuffer sb = new StringBuffer(); //ok
sb.append(str);//需要看源码 , 底层调用的是 AbstractStringBuilder 的 appendNull
System.out.println(sb.length());//4
System.out.println(sb);//null
//下面的构造器,会抛出 NullpointerException
StringBuffer sb1 = new StringBuffer(str);//看底层源码 super(str.length() + 16);//直接抛出空指针异常NullpointerException
System.out.println(sb1);
}
}
AbstractStringBuilder 的 appendNull
private AbstractStringBuilder appendNull() {
ensureCapacityInternal(count + 4);
int count = this.count;
byte[] val = this.value;
if (isLatin1()) {
val[count++] = 'n';
val[count++] = 'u';
val[count++] = 'l';
val[count++] = 'l';
} else {
count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
}
this.count = count;
return this;
}
appendNull() 函数会把 null 放在数组 value 中
StringBuilder
基本介绍
一个可变的字符序列.此类提供一个与 StringBuffer 兼容的 API ,但不保证同步(StringBuilder 不是线程安全).该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候.如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快.
在 StringBuilder 上的主要操作是 append 和 insert 方法.可重载这些方法,以接收任意类型的数据.
StringBuffer 的直接父类 是 AbstractStringBuilder
StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化
在父类中 AbstractStringBuilder 有属性 char[] value,不是 final
该 value 数组存放 字符串内容,引出存放在堆中的StringBuffer 是一个 final 类,不能被继承
因为 StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除)
不用每次都更换地址(即不是每次创建新对象), 所以效率高于 String
System类
exit 退出当前程序
- exit(0) 表示程序退出
- 0 表示一个状态 , 正常的状态
arraycopy
复制数组元素,比较适合底层调用,一般使用Arrays.copyOf
完成复制数组主要是搞清楚这五个参数的含义
源数组
* @param src the source array. // srcPos: 从源数组的哪个索引位置开始拷贝 // @param srcPos starting position in the source array. // dest : 目标数组,即把源数组的数据拷贝到哪个数组 // @param dest the destination array. // destPos: 把源数组的数据拷贝到 目标数组的哪个索引 // @param destPos starting position in the destination data. // length: 从源数组拷贝多少个数据到目标数组 // @param length the number of array elements to be copied | 含义 | 参数 | 内容 | | —- | —- | —- | | 源数组 | src | the source array | | 从源数组的哪个索引位置开始拷贝 | srcPos | starting position in the source array | | 目标数组,即把源数组的数据拷贝到哪个数组 | dest | the destination array | | 把源数组的数据拷贝到 目标数组的哪个索引 | destPos | starting position in the destination data. | | 从源数组拷贝多少个数据到目标数组 | length | the number of array elements to be copied |