目录
方式二:调用构造器创建 String s = new String("ww")
2、String、StringBuilder、StringBuffer的比较
18、包装类(Wrapper)
1、包装类的类型
针对八种基本数据类型相应的引用类型 ---包装类
有了类的特点,就可以调用类中的方法了
基本数据类型 | 包装类 |
---|---|
boolean | Boolean |
char | Charater |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
加粗的包装类的父类都是 Number
-
jdk5前是手动装箱和拆箱的 装箱(基本类型 -> 包装类型) 反之是拆箱
(只要是基本数据类型都可以char 和boolean也一样)
-
jdk5之后(含jdk5)就有了自动装箱和拆箱
-
自动装箱底层调用的是 valueOf()方法 比如int --> Integer 就是调用Integer.valueOf(int a);
4.自动拆箱底层调用的是 XXXValue()方法 比如 Integer --> int 就是调用new Integer().intValue(Integer i);
面试题
Object obj1 = true ? new Integer(1) : new Double(2.0);
System.out.println(obj1);
输出结果?
答案是 1.0 三元运算法是一个整体,会将new Integer() 的结果变成Double精度 所以出来的是1.0而不是1
2、包装类的常用方法
public class WrapperTest {
public static void main(String[]args){
//获取最小值
int minValue = Integer.MIN_VALUE;
//获取最大值
int maxValue = Integer.MAX_VALUE;
//判断是不是数字
boolean a = Character.isDigit('a');
//判断是不是字母
boolean b = Character.isLetter('b');
//判断是不是大写
boolean a1 = Character.isUpperCase('a');
//判断是不是小写
boolean c = Character.isLowerCase('c');
//判断是不是空格
boolean whitespace = Character.isWhitespace(' ');
//转换成大写
char a2 = Character.toUpperCase('a');
//转换成小写
char a3 = Character.toLowerCase('A');
}
}
3、Integer创建机制
经典面试题
public void method1() {
Integer i =new Integer(1);
Integer j =new Integer(1);
System.out.println(i == j); //false
Integer m = 1;
Integer n = 1;
System.out.println(m == n); //true
Integer x = 128;
Integer y = 128;
System.out.println(x == y); //false
}
== 引用数据类型比较的是地址是否相同
第一个 只要new就是创建一个新的对象 因此返回false
第二个 这个就涉及Integer 自动装箱的底层原理, 我们知道Integer自动装箱实际上是调用了ValueOf()方法
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; //如果不符合才创建新的对象 return new Integer(i); }
因此这个是关键
if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)];
通过源码可以看到
static final int low = -128; static final int high; static { h = 127 } /* 所以知道数字在-128 到127之间的数都是从 数组中取 因此如何符合这个范围 取到的都是同一个对象 因此 第二个 得到true 第三个128 不在这个范围 得到的是false */
习题二
Integer i = 127;
int j =127;
System.out.println(i == j)
//这里如果有基本数据类型,判断的是值是否相同 所以是true
19、String类
字符串字符使用Unicode字符编码,一个字符不管是字母还是汉字都是占两个字节
1、继承关系
-
String 是一个final修饰的类 代表String不能被继承
-
String的存储是通过 字符数组存储的,真正存储字符串内容的是通过这个数组
/** The value is used for character storage. 该值用于字符存储。 */ private final char value[];
-
注意这个 value是一个final类型 不可修改 --》是地址不可以修改 但是可以修改字符数组中内容的(但是这个字符数组还是private的,并且没有提供外部访问方法,因此只要字符串值改变了就是地址就发生了改变)
2、两种创建方式
方式一:直接赋值 String s = “ww”;
先从常量池查看是否有"ww"数据空间,如果有,直接指向;如果没有则重写创建,然后指向。S最终指向的是常量池中的空间地址
方式二:调用构造器创建 String s = new String("ww")
先在堆中创建空间,里面维护了value属性,指向常量池"ww"的空间。如果常量池没有,重新后仓健,如果有直接通过value指向。最终指向的是堆中空间地址
内存分析图
面试题
String a = "hello" + "world" ;
创建了几个对象?
只是创建了一个对象 helloworld
编译器不是傻子,这里做了优化,判断创建的常量池对象,是否有引用
(正常我们思路是创建三个 hello 、world、helloworld,但是hello和world没有引用,因此不创建 只创建一个helloworld)
面试题2
String a ="hello"
String b = "abc"
String c =a + b
创建了几个对象?并画出内存图
我认为创建了6个对象(不要纠结创建了几个对象,重点看内存分析)
常量池中 hello abc helloabc 三个对象
堆中创建了StringBuilder 、 创建了一个新的String赋值给了c 、然后StringBuilder还创建了char[]数组对象
证明代码:
public class WrapperTest {
public static void main(String[]args){
String a ="hello";
String b = "abc";
String c =a + b;
String d = "helloabc";
String e = "hello" + "abc";
System.out.println(c == d); //false
System.out.println(d == e);//true
}
}
小结
String c1 =“ab” + "cd" 就是常量池相加
String a + b 就是引用相加,是在堆中的
3.常用方法
-
equals 区分大小写,判断内容是否相等
-
equalsIgnoreCase 忽略大小写的判断内容是否相等
-
length 获取字符的个数, 即字符串长度
-
indexOf 获取字符在字符或者字符串在 字符串中第一次出现的索引,如果没有返回-1
-
lastIndexOf 获取字符或者字符串 在字符串中最后出现的索引 如果没有返回-1
-
substring 截取指定范围的子串
-
trim 去除前后空格
-
charAt 获取某索引出的字符,注意不能使用Str[index] 这个方式。
-
toUpperCase 转换为大写字母
-
toLowerCase 转换为小写字母
-
concat 连接字符串
-
replace 替换字符串中的字符
-
split 分割字符串,对应某些分割字符, 转换为字符串数组
-
compareTo 比较两个字符串大小
-
一个字符一个字符的进行比较,(c大于a 按照Asall码比较)
-
如果比较出来就返回 相减的值
-
如果长度和字符都一样则返回0
-
如果长度不相同,但是有的字符串都相同,返回长度差
-
-
toCharArray 转换为字符数组
-
format 格式化字符串 %s 字符串 %d整型 %.2f浮点型 %c字符
20、StringBuffer和StringBuilder
java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
很多方法与String相同,但是StringBuffer是可变长度的,StringBuffer也是一个容器
-
StringBuffer的直接父类是AbstractStringBuffer
-
StringBuffer实现了 Serializable,即StringBuffer对象可以串行化(序列化)进行网络传输
-
在父类 AbstractStringBuffer 有属性char[] value,不是final类型
-
该value数组存放我们字符串内容,因此存放在堆中 ,不在是常量池了
-
StringBuffer也是一个final类,不能被继承
-
因为StringBuffer里面的值可以改变,每次StringBuffer更新内容,不用每次更新地址,效率相较于String是比较高的
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str); //如果为null其实底层调用的是AbstractStringBuilder的appendNull()方法 将null加入进去
System.out.println(sb.length()) ; //因此输出的4
System.out.println(sb) ; //其实调用的toString方法 输出的是null字符串
StringBuffer sb1 = new StringBuffer(str); //这里底层调用的是 Super(str.length()+16);
//因此会出现异常 NullpointerException 空指针异常
StringBuilder与StringBuffer是类似的 ,只不过是StringBuilder是线程不安全的,效率更加高效
1、常用方法
-
增 append()
-
删 delete(start,end)
-
替换 replace(start,end,str)
-
查 indexOf(str) 查找字符串第一次出现的索引,如果没有返回-1
-
插入 insert()
-
获取长度 length()
2、String、StringBuilder、StringBuffer的比较
-
StringBuffer和StringBuilder非常相似,均代表可变的字符序列,而且方法是也一样
-
String:不可变字符序列,效率低,但是复用性高(常量池可以被多次使用)
-
StringBuffer可变字符序列,效率很高 是线程安全的
-
StringBuilder 可变字符序列 ,效率更高 ,是线程不安全的
如果频繁的修改String ,不建议使用String
使用结论
-
如果字符串存在大量的修改操作,一般使用StringBuffer或者StringBuilder
-
如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
-
如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
-
如果字符串很少修改,被多个对象引用,使用String ,比如配置信息等
21、Math类
常用方法 都是静态方法 使用类名. 调用即可
-
abs绝对值
-
pow求幂(几次方)
-
ceil先上取整
-
floor向下取整
-
round四舍五入
-
sqrt求开方
-
random求随机数([0,1]的随机数)
-
max 求两个数的最大值
-
min求两个数的最小值
22、Arrays类
Arrays类里面包含了一系列的静态方法,用于管理或操作数组(比如排序和搜索)
-
toString返回数组的字符串形式 Arrays.toString(arr)
public static void main(String[]args){ int[] arr = {1, 20, 90}; System.out.println(Arrays.toString(arr)); //[1, 20, 90] }
-
sort 排序 (自然排序和定制排序)
自然排序
public static void main(String[]args){ int[] arr = {1, -3, 4, 145, 90}; System.out.println(Arrays.toString(arr));//[1, -3, 4, 145, 90] //排序 Arrays.sort(arr); System.out.println(Arrays.toString(arr));//[-3, 1, 4, 90, 145] }
定制排序
public static void main(String[]args){ Integer[] arr = {1, -3, 4, 145, 90}; System.out.println(Arrays.toString(arr));//[1, -3, 4, 145, 90] //排序定制排序 传入两个参数 第一个参数是要排序的数组 第二个参数是比较器 //实现了Comparator接口的匿名内部类,要求实现compare接口 //o1 - o2 是升序排序 o2 - o1 是降序排序 Arrays.sort(arr, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } }); //lambda表达式可以写成 /* Arrays.sort(arr, (o1,o2) -> o1 - o2); */ System.out.println(Arrays.toString(arr));//[-3, 1, 4, 90, 145] }
-
binarySearch通过二分搜索法进行查找,要求必须排好序 Arrays.binarySearch(arr, target)
public static void main(String[]args){ Integer[] arr = {1, -3, 4, 145, 90}; Arrays.sort(arr); //使用二分搜索查找 必须是排好序的 //如果找到就返回索引 System.out.println(Arrays.binarySearch(arr, 4)); //2 //如果没有找到就返回 -(low + 1) low是应该存在的位置 //{-3,1,4,90,145} //22 在4 和 90 之间 如果存在就是在 4的索引的后一位 即3 System.out.println(Arrays.binarySearch(arr, 22)); //-4 //-4 在-3 之后 如果存在 low等于0 System.out.println(Arrays.binarySearch(arr, -4));//-1 }
-
copyOf 数组元素复制
public static void main(String[]args){ Integer[] arr = {1, -3, 4, 145, 90}; //从arr数组 拷贝arr.length到新数组中 Integer[] integers = Arrays.copyOf(arr, arr.length);//[1, -3, 4, 145, 90] Integer[] integers2 = Arrays.copyOf(arr, 2); //[1, -3] Integer[] integers3 = Arrays.copyOf(arr, arr.length+1); //[1, -3, 4, 145, 90, null] System.out.println(Arrays.toString(integers)); System.out.println(Arrays.toString(integers2)); System.out.println(Arrays.toString(integers3)); } 注入如果是第二个参数是负数 就会抛出异常 这个方法底层实际上使用的是System.arraycopy()
-
file 数组元素的填充
public static void main(String[]args){ Integer[] arr = {1, -3, 4, 145, 90}; //从arr数组 拷贝arr.length到新数组中 Arrays.fill(arr,99); System.out.println(Arrays.toString(arr)); //[99, 99, 99, 99, 99] }
-
equals 比较两个数组元素内容是否相同
public static void main(String[]args){ Integer[] arr = {1, -3, 4, 145, 90}; Integer[] arr2 = {1, -3, 4, 145, 90}; //从arr数组 拷贝arr.length到新数组中 Integer[] arr3= {22,2}; System.out.println(Arrays.equals(arr, arr2)); //true System.out.println(Arrays.equals(arr, arr3)); //false }
-
asList 将数组转换成List
public static void main(String[]args){ Integer[] arr = {1, -3, 4, 145, 90}; List<Integer> integers = Arrays.asList(arr); } 编译类型是 List 其实编译类型是ArrayList