目录
4.5 StringBuilder类 与 StringBuffer类
4.6 StringBuilder类 与 StringBuffer类的区别
一, 输入与输出
1.1 输入:
- Scanner in=new scanner(System.in):创造从控制台输入的标准输入对象
- Scanner in=new scanner(String):in对象只能读String的内容
- Scanner in=new scanner(new File("文件路径+文件名")):in对象读入文件中的内容
常用方法:
- boolean hasNext():检测输入中是否还有其他单词
- boolean hasNextInt():检测输入中是否还有整数
- boolean hasNextDouble():检测输入中是否还有整数或浮点数
举例:
备注: 注意先输入nextInt(), 后输入nextLine的陷阱:
1.2 输出:
System.out.println(msg); // 输出一个字符串, 带换行
System.out.print(msg); // 输出一个字符串, 不带换行
System.out.printf(format, msg); // 格式化输出
转换符
|
类型
|
举例
| 输出 |
---|---|---|---|
d
|
十进制整数
|
printf("%d", 100)
| 100 |
s
|
字符串
|
printf("%s", "abc")
| abc |
c
|
字符
| printf("%c", ‘a’) | a |
f
|
定点浮点数
|
printf("%f", 100f)
| 100.000000 |
二, 控制流
2.1 条件语句:
基本语法1:
if(布尔表达式){
//条件满足时执行代码
}
基本语法2:
if(布尔表达式){
//条件满足时执行代码
}else{
//条件不满足时执行代码
}
基本语法3:
if(布尔表达式){
//条件满足时执行代码
}else if(布尔表达式){
//条件满足时执行代码
}else{
//条件都不满足时执行代码
}
2.2 switch 语句
switch(整数|枚举|字符|字符串){
case 内容1 : 内容满足时执行语句;break;
case 内容2 : 内容满足时执行语句;break;
...
default:内容都不满足时执行语句;
}
例子:
int day = 1;
switch(day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期日");
break;
default:
System.out.println("输入有误");
break;
}
输出结果为: 星期一
备注: 没有break语句的话,case 1: 满足后,case 2,case 3等都会被执行。
2.3 循环语句
while循环:
while(循环条件){
循环语句;
}
do...while循环:
do{
循环语句;
}while(循环条件);
for循环:
for(表达式1;表达式2;表达式3){
循环体;
}
表达式1: 用于初始化循环变量.
表达式2: 循环条件
表达式3: 更新循环变量
foreach 循环:
for(数据类型 变量名:数组名) {
需要执行的语句块;
}
2.4 中断控制流语句:
break:直接退出循环
continue:直接进入下一次循环,跳过本次循环continue语句之后剩余的语句
带标签的break语句:
三 数组
什么是数组:数组本质上就是让我们能 "批量" 创建相同类型的变量.
3.1 创建数组
// 动态初始化
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
// 静态初始化
数据类型[] 数组名称 = { 初始化数据 };
//声明数组
数据类型[] 数组名称= new 数据类型[数组长度n];
如:
int[] arr = new int[]{1, 2, 3};
int[] arr = {1, 2, 3};
char[] arr = new char[10];
3.2 使用数组
通过下标使用:
int[] arr = {1, 2, 3};
// 获取数组长度
System.out.println("length: " + arr.length); // 执行结果: length: 3
// 访问数组中的元素
System.out.println(arr[1]); // 执行结果: 2
System.out.println(arr[0]); // 执行结果: 1
arr[2] = 100;
System.out.println(arr[2]); // 执行结果: 100
int[] arr = {1, 2, 3};
for (int x : arr) {
System.out.println(x);
}
// 执行结果:
1
2
3
3.3 数组的排序:
升序排序:
public class Bb {
public static void main(String[] args) {
int[] nums={3,6,9,2,5,8,1,4,7};
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));\\输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]
}
}
Arrays.sort():Java库函数,默认升序排序
Arrays.toString(nums): 返回指定数组的内容的字符串表示形式
重写排序,降序排序:(只适用于包装类对象)
3.4 数组拷贝与引用的区别:
引用:引用是指变量中存的不是值本身,而是存放着值所在的内存地址
int[] nums = {3, 6, 9, 2, 5, 8, 1, 4, 7};
int[] a = nums;
System.out.println(Arrays.toString(a));\\输出:[3, 6, 9, 2, 5, 8, 1, 4, 7]
拷贝:
int[] nums = {3, 6, 9, 2, 5, 8, 1, 4, 7};
int[] b = Arrays.copyOf(nums, nums.length);
System.out.println(Arrays.toString(b)); \\输出:[3, 6, 9, 2, 5, 8, 1, 4, 7]
区别:
原因:(简单示意图)
3.5 二维数组:
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 }
示例 :
int[][] arr = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
}
// 执行结果 :
1 2 3 4
5 6 7 8
9 10 11 1
理解:
四, String类
4.1 创建字符串
// 方式一
String str = "Hello Bit";
// 方式二
String str2 = new String("Hello Bit");
// 方式三
char[] array = {'a', 'b', 'c'};
String str3 = new String(array);
- "hello" 这样的字符串字面值常量, 类型也是 String.
- String 也是引用类型. String str = "Hello"; 这样的代码内存布局如下
假设String str2=str,那么str2="world",str是不是也等于"world"呢?
结果:
原因:
事实上, str1 = "world" 这样的代码并不算 "修改" 字符串, 而是让 str1 这个引用指向了一个新的 String 对象
4.2 字符串比较相等
回顾一下int类型我们怎么比较的:
int x = 10 ;
int y = 10 ;
System.out.println(x == y);\\输出: true
那么String呢? 用以上方式试试:
乍一看是对的,实际上这样比较是错误的,原因是因为String是引用类型,他们比较的是str1与str2中地址是否相同,而碰巧,他们是相同的:
比较结果是str1和str2的值都为0x333,所以相同。而实际上我们想比较的是引用所指向的内容,即"hello"是不是等于"hello",接下来看这一段代码:
原因:
str2中的值为0x444 不等于 str1中的值,因此两者不相等
因此,在Java中,比较对象所引用的值不能简单的用==来比较, 而是要用到一些相关的方法,如String类中的equals方法
如:
使用注意事项: 方式二更合适, 因为当str=null时,方式一会出现异常
// 方式一
System.out.println(str.equals("Hello"));
// 方式二
System.out.println("Hello".equals(str));
4.3 理解字符串不可变
我们来看一下String类的原码:
String类的一个成员变量是一个char类型的数组,并且被final修饰,表示value数组内容不可变
那下面这段代码如何实训的呢?
String str="hello";
str+="!!!";
System.out.println(str); //输出: hello!!!
我们来看一下编译时,他是如何运行的:
从中我们可以看到,在拼接字符串的时候,是new了一个StringBuilder类,通过append()方法来将"hello"和"!!!"进行拼接,最后通过toStirng()方法转化为字符串,最后赋值给str引用
在内存图中,是这样的:
4.4 字符串常量池
在上述例子中:
String str1 = new String("hello") ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); //输出: false
System.out.println(str2 == str3); //输出: true
为什么str1!=str2,而str2==str3呢?
String 类的设计使用了 共享设计模式因为在 JVM 底层实际上会自动维护一个对象池(字符串常量池 )
- 如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存 到这个对象池之中.
- 如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用
- 如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用
原因如图:
通过调试,我们可以验证value引用指向的是同一个0x123的字符串地址
要注意的是:
String str1 = new String("hello") ; //匿名对象,不在字符串常量池中
String str2 = "hello" ; //在字符串常量池中
System.out.println(str1 == str2); //输出结果为false;
我们可以通过intern()方法来将方法来将 str1的匿名对象加入到字符串常量池中:
String str1 = new String("hello").intern() ; //加入到字符串常量池
String str2 = "hello" ; //使用字符串常量池中的地址
System.out.println(str1 == str2); //输出true
面试题:请解释 String 类中两种对象实例化的区别1. 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。2. 构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用 intern() 方法手工入池。
4.5 StringBuilder类 与 StringBuffer类
前面我们解释String类是不能改变的, 只有通过StringBuilder和StringBuffer类才能进行拼接, 而直接拼接String类会,底层编译会帮我们自动new一个StringBulider类进行拼接
这段代码:
String str="hello";
str+="!!!";
等价于:
因此,但需要大量拼接字符串时,建议使用StringBuilder类 与 StringBuffer类, 这样减少创建这两个类是时间消耗。
验证:用String拼接10万次"a"与StringBuilder拼接10万次"a"分别需要的时间:
而且, StringBuilder类 与 StringBuffer类有String没有的方法:
4.6 StringBuilder类 与 StringBuffer类的区别
主要区别体现在原码上:
方法上,几乎一样,但StringBuffer是线程安全的,StringBuilder不是线程安全,但运行更快。synchronized 表示线程安全。
最后:class类名的首字母建议大写,截图有不当之处,望周知!