赞
踩
大家好呀!我是小笙!我学习了韩顺平老师的常用类的知识,收获颇丰!现在来和大家分享笔记!
包装类 | 基本数据类型 | 直接父类 |
---|---|---|
boolean | Boolean | Object |
char | Character | Object |
byte | Byte | Number |
short | Short | Number |
int | Int | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
Boolean
Character
Number父类下的直接子类
public class Wrapper01 { public static void main(String[] args) { // jdk5以前手动装箱&手动拆箱;jdk5之后可以自动拆装箱 // 以Character为例 char name = 'n'; // 手动装箱 Character ch1 = new Character(name); // 不推荐 Character ch2 = Character.valueOf(name); // 手动拆箱 char name2 = Character.valueOf(ch2); // 本质就是使用charValue方法 char name3 = ch1.charValue(); // 自动装箱 Character ch3 = name; // 本质使用的就是valueOf方法 // 自动拆箱 char CH4 = ch3; // 本质就是使用charValue方法 } }
接下来我对于自动拆装箱的底层进行追踪结果
首先打四个断点,分别探索这四个断点的跳转
以下是依次跳转的函数
总结
习题
// 如下输出结果是什么
习题1
Object obj = true? new Integer(1):new Double(2.0); // 三元运算符是一个整体
System.out.println(obj); // 1.0
习题2
Object obj1;
if(true){
obj1 = new Integer(1);
}else{
obj1 = new Double(2.0);
}
System.out.println(obj); // 1
public class WrapperVsString { public static void main(String[]args){ // String类 转换成 包装类 String age = "120"; Integer age2 = Integer.valueOf(age); // 方式一:valueOf函数 本质上就是parseInt()方法 Integer a2 = Integer.parseInt(age); // 方式二:parseInt函数 Integer age3 = new Integer(age); //不推荐,本质就是parseInt()方法 // 包装类 转换成 String类 Integer height = 180; // 自动装箱 String h = String.valueOf(height); // 方式一:valueOf函数 本质就是调用toString()方法 String h2 = height + ""; // 方式二: 类型转换 Integer + "" String h3 = height.toString(); // 方式三: toString()函数 /* * String.valueOf()源码 * public static String valueOf(Object obj) { * return (obj == null) ? "null" : obj.toString(); * } * * Integer.valueOf()源码 * public static Integer valueOf(String s) throws NumberFormatException { * return Integer.valueOf(parseInt(s, 10)); // 10指的是传入的数字是十进制数 * } * * new Integer()源码 * @Deprecated(since="9") * public Integer(String s) throws NumberFormatException { * this.value = parseInt(s, 10); * } */ } }
以Integer包装类为例
包装类的相关面试题
public class Wrapper02 { public static void main(String[] args) { /* * 源码:IntegerCache.low -128 IntegerCache.high 127 * public static Integer valueOf(int i) { * if (i >= IntegerCache.low && i <= IntegerCache.high) * return IntegerCache.cache[i + (-IntegerCache.low)]; * return new Integer(i); * } * 如果valueOf(value) value > -128 && value < 127 则 返回 IntegerCache.cache[i + (-IntegerCache.low)] * 否则 返回新对象Integer */ System.out.println(new Integer(1) == new Integer(1)); // false Integer a = 1; Integer b = 1; System.out.println(a==b); // true Integer m = 128; Integer n = 128; System.out.println(m==n); // false Integer x = 128; int y = 128; System.out.println(x==y); // true } }
public static void main(String[] args) {
/**
* String
* 概念:是一组字符序列 本质上是char[] value 字符数组实现
* "Al_tair"被称为字符常量 用双引号括起来的字符序列
* 一个字符占用两个字节(每个字符不区分字母和汉字)
* public final class String 说明String的final类,不能被其它类继承
* private final byte[] value 用于存放字符串 value是用final修饰的类型,该数组不能指向新地址,但是能修改它的值
*/
String name = "Al_tair";
}
// 运行代码,内存图如下
class code{
public static void main(String[] args){
String a = "Al_tair";
String b = new String("Al_tair");
}
}
内存图: 字符串 VS 字符数组
结合代码和内存图分析
class Text{
String str = new String("lns");
// final指的是char类型数据存储的地址不能改变,但是值是可以改变的
final char[] ch = {'j','a','v','a'};
public void change(String str,char[] ch){
str = "zlr";
ch[1] = 'c';
}
public static void main(String[] args) {
Text text = new Text();
text.change(text.str,text.ch);
System.out.println(text.str.toString()+" and "+text.ch[1]); // lnsandc
}
}
// equals()方法源码 public boolean equals(Object anObject) { if (this == anObject) { // 地址是否相同 return true; } if (anObject instanceof String) { // 是否为String类或者String父类 String aString = (String)anObject; if (!COMPACT_STRINGS || this.coder == aString.coder) { return StringLatin1.equals(value, aString.value); } } return false; } @HotSpotIntrinsicCandidate public static boolean equals(byte[] value, byte[] other) { if (value.length == other.length) { for (int i = 0; i < value.length; i++) { if (value[i] != other[i]) { return false; } } return true; } return false; } // 占位符的讲解 涉及方法format <=> c语言输出 // %s,%d,%.3f,%c String name = "lns"; int age = 18; double height = 185.35; char gender = '男'; String Info = "姓名:%s\t年龄:%d\t身高:%.3f\t性别:%c"; String show = String.format(Info,name,age,height,gender); System.out.println(show); // 姓名:lns 年龄:18 身高:185.350 性别:男
// 习题1 String a = "l"; String b = new String("l"); System.out.println(a.equals(b)); // true System.out.println(a == b); // false System.out.println(a == b.intern()); // true System.out.println(b == b.intern()); // false // 习题2 // 2.1创建了几个对象 答:2 String s = "hello"; s = "haha"; // 2.2 创建了几个对象 答:1 结论:编译器会做优化,判断常量池对象是否有引用指向 String str = "hello" + "haha"; // 等价于 String str = "hellohaha"; // 2.3 创建了几个对象 答:3 结论:字符串常量相加地址存放在常量池,字符串变量相加地址存放在String对象中 // sum 指向的是value[](String对象),再指向常量池中"HelloString"字符串 public static void main(String[]args){ String m = "Hello"; String n = "String"; /* * 解读: * 1. 创建新对象 new StringBuilder(); * 2. 通过append函数添加字符串 “Hello” * 3. 通过append函数添加字符串 “String” * 4. 返回new String("HelloString"); */ String sum = m + n; } // 分析sum 的指向和底层源码 // debug test // first insert public StringBuilder() { super(16); } //secong insert str = "Hello" public StringBuilder append(String str) { super.append(str); return this; } // third insert str = "String" public StringBuilder append(String str) { super.append(str); return this; } // last one public String toString() { // Create a copy, don't share the array return isLatin1() ? StringLatin1.newString(value, 0, count): StringUTF16.newString(value, 0, count); }
概念:代表可变的字符序列,可以对字符串内容进行增删,是一个容器
Constructor and Description |
---|
StringBuffer() 构造一个没有字符的字符串缓冲区,初始容量为16个字符。 |
StringBuffer(CharSequence seq) 构造一个包含与指定的相同字符的字符串缓冲区 CharSequence 。 |
StringBuffer(int capacity) 构造一个没有字符的字符串缓冲区和指定的初始容量。 |
StringBuffer(String str) 构造一个初始化为指定字符串内容的字符串缓冲区。 |
/*
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
* StringBuffer()构造器
*/
@HotSpotIntrinsicCandidate
public StringBuffer() {
super(16); // 初始容量为16个字符 存储在父类的value数组中
}
String类和StringBuffer类的区别
String类和StringBuffer类的相互转换
public static void main(String[] args) {
// String和StringBuffer的相互转换
// String => StringBuffer
String str = "lns";
StringBuffer stringBuffer = new StringBuffer(str); // 方式一: 使用StringBuffer构造器
StringBuffer append = new StringBuffer().append(str); // 方式二: 使用的是append方法
// StringBuffer => String
StringBuffer sbr = new StringBuffer("zlr");
String s = sbr.toString(); // 方式一: 使用toString方法
String s1 = new String(sbr); // 使用String构造器
}
public static void main(String[] args) { // 常用方法 // append 增 StringBuffer stringBuffer = new StringBuffer(""); stringBuffer.append("lns"); // lns /* * append源码 * 不管传入什么数据类型,返回StringBuffer类型 * public synchronized StringBuffer append(String str) { * toStringCache = null; * super.append(str); * return this; * } */ // delete 删除 // 删除索引范围 [start,end) stringBuffer.delete(0,1); // 删除第一个字符 ns // replace 替换 // 替换范围[start,end) stringBuffer.replace(0, 1,"ln"); // lns // indexOf 查找 // 查找第一次在字符串中出现的索引,如果查找到会返回你查找的字符串首个字母索引,如果找不到返回-1 stringBuffer.indexOf("ns"); // 1 // length 长度 System.out.println(stringBuffer.length()); // 3 }
// 习题1 String str = null; StringBuffer sb = new StringBuffer(); sb.append(str); System.out.println(sb); // null System.out.println(sb.length()); // 4 /* * // 底层分析 * // StingBuffer类 * public synchronized StringBuffer append(String str) { * toStringCache = null; * super.append(str); // 跳转到父类 * return this; * } * // AbstractStringBuilder抽象类 * public AbstractStringBuilder append(String str) { * if (str == null) { * return appendNull(); // 跳转到该方法 * } * int len = str.length(); * ensureCapacityInternal(count + len); * putStringAt(count, str); * count += len; * return this; * } * // 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; * } */ StringBuffer sb = new StringBuffer(str); // 抛出空指针异常 NullPointerException /* * AbstractStringBuilder(String str) { * int length = str.length(); // str为null * int capacity = (length < Integer.MAX_VALUE - 16) * ? length + 16 : Integer.MAX_VALUE; * final byte initCoder = str.coder(); * coder = initCoder; * value = (initCoder == LATIN1) * ? new byte[capacity] : StringUTF16.newBytesFor(capacity); * append(str); * } */
概念:一个可变的字符序列。 线程不安全。 此类设计用作简易替换为StringBuffer
在正在使用由单个线程字符串缓冲区的地方。 在可以的情况下,建议使用这个类别优先于StringBuffer
,因为它在大多数实现中将更快。
大部分与 StringBuffer类似
特殊点:没有做互斥处理,因此在单线程下使用
// 源码剖析 区别在于关键字 synchronized 保证线程安全 // StringBuffer 的append方法 @Override @HotSpotIntrinsicCandidate public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } // StringBuilder 的append方法 @Override @HotSpotIntrinsicCandidate public StringBuilder append(String str) { super.append(str); return this; }
String,StringBuffer,StringBuilder的区别
使用原则
概念:Math类包含执行基本数学运算的方法
public static void main(String[] args) { // Math类中大部分是静态方法,可以直接通过类名.方法名访问 // abs 绝对值 int abs = Math.abs(-10); System.out.println(abs); // 10 // pow 求幂 double pow = Math.pow(2,4); System.out.println(pow); // 16.0 // ceil 向上取整,返回>=该参数的最小整数(整数会转换成double型) double ceil = Math.ceil(-3.002); System.out.println(ceil); // -3.0 // floor 向下取整,返回<=该参数的最大整数(整数会转换成double型) double floor = Math.floor(3.2); System.out.println(floor); // 3.0 // round 四舍五入 <=> Math.floor(参数+0.5) double round = Math.round(3.24); System.out.println(round); // 3.0 // sqrt 求开平方 double sqrt = Math.sqrt(4); System.out.println(sqrt); // 2.0 // random 随机数 [0,1) int random = (int)(Math.random()*50+50); System.out.println(random); // 整数范围 [50,100) }
概念:该类包含用于操作数组的各种方法(如排序和搜索),大部分方法也是静态方法
作用:输出数组
Integer[] array = {3,5,6,47,8}; // toString 输出数组 System.out.println(Arrays.toString(array)); // [3, 5, 6, 47, 8] /* * // toString方法源码 * public static String toString(int[] a) { * if (a == null) * return "null"; * int iMax = a.length - 1; * if (iMax == -1) * return "[]"; * * StringBuilder b = new StringBuilder(); * b.append('['); * for (int i = 0; ; i++) { * b.append(a[i]); * if (i == iMax) * return b.append(']').toString(); * b.append(", "); * } * } */
作用:排序数组默认从小到大
// sort重载,可以通过传入一个接口Comparator实现定制排序 Integer[] array = {3,5,6,47,8}; Arrays.sort(array); System.out.println(Arrays.toString(array)); // [3, 5, 6, 8, 47] Arrays.sort(array,new Comparator(){ @Override public int compare(Object o1, Object o2) { Integer i1 = (Integer)o1; Integer i2 = (Integer)o2; return i2 - i1; // 决定是升序还是降序 } }); System.out.println(Arrays.toString(array)); // [47, 8, 6, 5, 3] /** * MySort的冒泡实现 * public class MySort { * public static void main(String[] args) { * int[] arr = {6,4,5,6,845,4,51}; * bubble(arr, new Comparator() { * @Override * public int compare(Object o1, Object o2) { * int i1 = (Integer)o1; * int i2 = (Integer)o2; * return i1 - i2; * } * }); * System.out.println(Arrays.toString(arr)); * } * * public static void bubble(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++) { * if(c.compare(arr[j],arr[j+1]) >= 0){ * temp = arr[j]; * arr[j] = arr[j+1]; * arr[j+1] = temp; * } * } * } * } * } */
作用:通过二分搜索法进行查找,要求必须升序,如果数组中不存在,则返回 -(应该在的索引位置 + 1)
Integer[] array = {3,5,6,47,8}; Arrays.sort(array); // [3, 5, 6, 8, 47] int index = Arrays.binarySearch(array,9); System.out.println(index); // -5 应该在索引4位置(8和471之间),返回-(4+1) /** * binarySearch 源码 * private static int binarySearch0(Object[] a, int fromIndex, int toIndex, Object key) { * int low = fromIndex; * int high = toIndex - 1; * * while (low <= high) { * int mid = (low + high) >>> 1; * @SuppressWarnings("rawtypes") // 抑制警告 * Comparable midVal = (Comparable)a[mid]; * @SuppressWarnings("unchecked") * int cmp = midVal.compareTo(key); * * if (cmp < 0) * low = mid + 1; * else if (cmp > 0) * high = mid - 1; * else * return mid; // key found * } * return -(low + 1); // key not found. * } */
// copeOf 数组的赋值 如果赋值的长度大于原数组的长度,则多余的数据用null填入
Integer[] integers = Arrays.copyOf(array, array.length-1);
System.out.println(Arrays.toString(integers)); // [3, 5, 6, 8]
// fill 数组的填充 替换数组中的所有数据
int[] fillNum = {2,45,78,85,15};
Arrays.fill(fillNum,2);
System.out.println(Arrays.toString(fillNum)); // [2, 2, 2, 2, 2]
// equals 比较两个数组元素内容是否相同
int[] equalsNum = {2,45,78,85,15};
int[] equalsNum2 = {2,45,78,85,15};
System.out.println(Arrays.equals(equalsNum,equalsNum2)); // true
概念:System
类包含几个有用的类字段和方法。 它不能被实例化。
public static void main(String[] args) { // gc 方法 垃圾回收器 new System01(); System.gc(); // 我已经被销毁了... // currentTimeMillis 方法 在1970年1月1日UTC之间的当前时间和午夜之间的差异,以毫秒为单位。 System.out.println(System.currentTimeMillis()); // 1645776480314 // arraycopy 方法 复制数组 int[] src = {1,2,3}; int[] desc = {0,0,0}; /* * 从左到右的五个参数描述 * 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. 拷贝的数组长度 */ System.arraycopy(src,0,desc,0,3); System.out.println(Arrays.toString(desc)); //[1, 2, 3] System.out.println(src == desc); // false // exit 方法 退出 System.out.println("程序开始"); /* * status说明例子 * 在一个if-else判断中,如果我们程式是按照我们预想的执行, * 到最后我们需要停止程式,那么我们使用System.exit(0), * 而System.exit(1)一般放在catch块中,当捕获到异常,需要停止程式, * 我们使用System.exit(1)。这个status=1是用来表示这个程式是非正常退出。 */ System.exit(0); // System.exit(0)是正常退出程序,而System.exit(1)或者说非0表示非正常退出程序 System.out.println("程序结束"); // 不执行 } @Override protected void finalize(){ System.out.println("我已经被销毁了..."); }
概念:BigIneger 适合保存比较大的整型数据;BigDecimal 适合保存精度更高的浮点型数据
// BigIneger 适合保存比较大的整型数据 long数据类型无法存储 BigInteger bigInteger = new BigInteger("998456349564561256465489"); System.out.println(bigInteger); // 998456349564561256465489 // + - * / 运算 => 方法实现 add subtract multiply divide bigInteger = bigInteger.add(new BigInteger("1")); System.out.println(bigInteger); // 998456349564561256465490 bigInteger = bigInteger.divide(new BigInteger("2")); System.out.println(bigInteger); // 499228174782280628232745 bigInteger = bigInteger.subtract(new BigInteger("2")); System.out.println(bigInteger); // 499228174782280628232743 bigInteger = bigInteger.multiply(new BigInteger("2")); System.out.println(bigInteger); // 998456349564561256465486 // BigDecimal 适合保存精度更高的浮点数 double数据类型无法存储 BigDecimal bigDecimal = new BigDecimal("9980.2561295645485648548485646541"); System.out.println(bigDecimal); // 9980.2561295645485648548485646541 // + - * / 运算 => 方法实现 add subtract multiply divide bigDecimal = bigDecimal.add(new BigDecimal("1")); System.out.println(bigDecimal); // 9981.2561295645485648548485646541 bigDecimal = bigDecimal.divide(new BigDecimal("2")); // 如果除不尽则返回算术异常 System.out.println(bigDecimal); // 4990.62806478227428242742428232705 bigDecimal = bigDecimal.subtract(new BigDecimal("2")); System.out.println(bigDecimal); // 4988.62806478227428242742428232705 bigDecimal = bigDecimal.multiply(new BigDecimal("2")); System.out.println(bigDecimal); // 9977.25612956454856485484856465410 // 解决小数除法异常问题:指定精度(JDK9以后不建议使用) bigDecimal = bigDecimal.divide(new BigDecimal("2.3326"),BigDecimal.ROUND_CEILING); System.out.println(bigDecimal); // 4277.31121047952866537548167909376
Date:精确到毫秒,代表瞬间
SimpleDateFormat:格式和解析日期类(日期 <=> 文本)
public static void main(String[] args) throws ParseException {
// Date 日期类
Date date = new Date(); // 当前日期
System.out.println(date); // Fri Feb 25 16:58:51 CST 2022
Date date2 = new Date(4564956); // 输入距离1970年1月1日的毫秒数
System.out.println(date2); // Thu Jan 01 09:16:04 CST 1970
// SimpleDateFormat 格式和解析日期类 按照自己的格式的日期 年 月 日 时 分 秒 星期 (规定如下图)
SimpleDateFormat sdf = new SimpleDateFormat("YYYY年MM月DD日 hh:mm:ss E");
System.out.println(sdf.format(date)); // 2022年02月56日 05:07:32 周五
String dateStr = "2021年02月56日 05:07:32 周一";
System.out.println(sdf.format(sdf.parse(dateStr))); // 2021年12月363日 05:07:32 周一 会存在编译异常
}
SimpleDateFormat的规定格式
Calendar类(日历) 是一个抽象类
// 抽象类 可以通过getInstance方法获取实例
Calendar calendar = Calendar.getInstance();
System.out.println(calendar);
System.out.println("年:"+calendar.get(calendar.YEAR)); // 年:2022
System.out.println("月:"+calendar.get(calendar.MONTH)+1); // 月:2 源码:JANUARY} which is 0
System.out.println("日:"+calendar.get(calendar.DAY_OF_MONTH)); // 日:25
System.out.println("小时:"+calendar.get(calendar.HOUR)); // 小时:8
System.out.println("分钟:"+calendar.get(calendar.MINUTE)); // 分钟:11
System.out.println("秒:"+calendar.get(calendar.SECOND)); // 秒:46
LocalDate 日期:年月日
LocalTime 时间:时分秒
LocalDateTime:年月日 时分秒
LocalDateTime localDateTime = LocalDateTime.now();
LocalTime localTime = LocalTime.now();
LocalDate localDate = LocalDate.now();
// localDateTime: 2022-02-25T20:30:19.250574 LocalTime: 20:30:19.250574 LocalDate: 2022-02-25
System.out.println("localDateTime: "+localDateTime+" LocalTime: "+
localTime+" LocalDate: "+localDate);
System.out.println("年: "+localDateTime.getYear()); // 年: 2022
System.out.println("月: "+localDateTime.getMonth()); // 月: FEBRUARY
System.out.println("日: "+localDateTime.getDayOfMonth()); // 日: 25
System.out.println("时: "+localDateTime.getHour()); // 时: 20
System.out.println("分: "+localDateTime.getMinute()); // 分: 33
System.out.println("秒: "+localDateTime.getSecond()); // 秒: 45
DateTimeFormatter格式日期类
// DateTimeFormatter 格式日期类
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY年MM月DD日 hh:mm:ss E");
System.out.println(dateTimeFormatter.format(localDateTime)); // 2022年02月56日 08:39:43 周五
// 所有字母“A”至“Z”和“a”至“z”保留为图案字母。 定义了以下图案字母: Symbol Meaning Presentation Examples ------ ------- ------------ ------- G era text AD; Anno Domini; A u year year 2004; 04 y year-of-era year 2004; 04 D day-of-year number 189 M/L month-of-year number/text 7; 07; Jul; July; J d day-of-month number 10 Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter Y week-based-year year 1996; 96 w week-of-week-based-year number 27 W week-of-month number 4 E day-of-week text Tue; Tuesday; T e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T F week-of-month number 3 a am-pm-of-day text PM h clock-hour-of-am-pm (1-12) number 12 K hour-of-am-pm (0-11) number 0 k clock-hour-of-am-pm (1-24) number 0 H hour-of-day (0-23) number 0 m minute-of-hour number 30 s second-of-minute number 55 S fraction-of-second fraction 978 A milli-of-day number 1234 n nano-of-second number 987654321 N nano-of-day number 1234000000 V time-zone ID zone-id America/Los_Angeles; Z; -08:30 z time-zone name zone-name Pacific Standard Time; PST O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00; X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15; x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15; Z zone-offset offset-Z +0000; -0800; -08:00; p pad next pad modifier 1 ' escape for text delimiter '' single quote literal ' [ optional section start ] optional section end # reserved for future use { reserved for future use } reserved for future use
Instant 时间戳
// Instant -> Date
Instant instant = Instant.now();
System.out.println(instant); // 2022-02-25T14:48:47.557358800Z
java.util.Date from = Date.from(instant);
System.out.println(from); // Fri Feb 25 22:48:47 CST 2022
// Date -> Instant
Instant instant1 = from.toInstant();
System.out.println(instant1); // 2022-02-25T14:55:27.377Z
相关面试题
1.String类有哪些方法?
String类是Java最常用的API,它包含了大量处理字符串的方法,比较常用的有:
2.String可以被继承吗?
String类由final修饰,所以不能被继承。
扩展阅读
在Java中,String类被设计为不可变类,主要表现在它保存字符串的成员变量是final的。
之所以要把String类设计为不可变类,主要是出于安全和性能的考虑,可归纳为如下4点。
因为要保证String类的不可变,那么将这个类定义为final的就很容易理解了。如果没有final修饰,那么就会存在String的子类,这些子类可以重写String类的方法,强行改变字符串的值,这便违背了String类设计的初衷。
3.说一说String和StringBuffer有什么区别
String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。
4.说一说StringBuffer和StringBuilder有什么区别
tringBuffer、StringBuilder都代表可变的字符串对象,它们有共同的父类 AbstractStringBuilder,并且两个类的构造方法和成员方法也基本相同。不同的是,StringBuffer是线程安全的,而StringBuilder是非线程安全的,所以StringBuilder性能略高。一般情况下,要创建一个内容可变的字符串,建议优先考虑StringBuilder类。
5.使用字符串时,new和""推荐使用哪种方式?
先看看 “hello” 和 new String(“hello”) 的区别:
显然,采用new的方式会多创建一个对象出来,会占用更多的内存,所以一般建议使用直接量的方式创建字符串。
6.两个字符串相加的底层是如何实现的?
如果拼接的都是字符串直接量,则在编译时编译器会将其直接优化为一个完整的字符串,和你直接写一个完整的字符串是一样的。
如果拼接的字符串中包含变量,则在编译时编译器采用StringBuilder对其进行优化,即自动创建StringBuilder实例并调用其append()方法,将这些字符串拼接在一起。
7.遇到过异常吗,如何处理?
在Java中,可以按照如下三个步骤处理异常:
捕获异常
将业务代码包裹在try块内部,当业务代码中发生任何异常时,系统都会为此异常创建一个异常对象。创建异常对象之后,JVM会在try块之后寻找可以处理它的catch块,并将异常对象交给这个catch块处理。
处理异常
在catch块中处理异常时,应该先记录日志,便于以后追溯这个异常。然后根据异常的类型、结合当前的业务情况,进行相应的处理。比如,给变量赋予一个默认值、直接返回空值、向外抛出一个新的业务异常交给调用者处理,等等。
回收资源
如果业务代码打开了某个资源,比如数据库连接、网络连接、磁盘文件等,则需要在这段业务代码执行完毕后关闭这项资源。并且,无论是否发生异常,都要尝试关闭这项资源。将关闭资源的代码写在finally块内,可以满足这种需求,即无论是否发生异常,finally块内的代码总会被执行。
8.请介绍Java的异常接口
Throwable是异常的顶层父类,代表所有的非正常情况。它有两个直接子类,分别是Error、Exception。
Error是错误,一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图使用catch块来捕获Error对象。在定义方法时,也无须在其throws子句中声明该方法可能抛出Error及其任何子类。
Exception是异常,它被分为两大类,分别是Checked异常和Runtime异常。所有的RuntimeException类及其子类的实例被称为Runtime异常;不是RuntimeException类及其子类的异常实例则被称为Checked异常。Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。如果程序没有处理Checked异常,该程序在编译时就会发生错误,无法通过编译。Runtime异常则更加灵活,Runtime异常无须显式声明抛出,如果程序需要捕获Runtime异常,也可以使用try…catch块来实现。
9.int和Integer有什么区别,二者在做==运算时会得到什么结果?
int是基本数据类型,Integer是int的包装类。二者在做==运算时,Integer会自动拆箱为int类型,然后再进行比较。届时,如果两个int值相等则返回true,否则就返回false。
10.说一说自动装箱、自动拆箱的应用场景
自动装箱、自动拆箱是JDK1.5提供的功能。
自动装箱:可以把一个基本类型的数据直接赋值给对应的包装类型;
自动拆箱:可以把一个包装类型的对象直接赋值给对应的基本类型;
通过自动装箱、自动拆箱功能,可以大大简化基本类型变量和包装类对象之间的转换过程。比如,某个方法的参数类型为包装类型,调用时我们所持有的数据却是基本类型的值,则可以不做任何特殊的处理,直接将这个基本类型的值传入给方法即可。
11.为啥要有包装类?
Java语言是面向对象的语言,其设计理念是“一切皆对象”。但8种基本数据类型却出现了例外,它们不具备对象的特性。正是为了解决这个问题,Java为每个基本数据类型都定义了一个对应的引用类型,这就是包装类。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。