API基础第一天:
笔记:
-
String:字符串类型
- java.lang.String使用的final修饰,不能被继承
- 字符串底层封装了字符数组以及针对字符数组的操作算法
- Java字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的编码
- 字符串一旦创建,对象内容永远无法改变,但字符串引用可以重新赋值
-
常量池:
- java对字符串有一个优化措施:字符串常量池(堆中)
- java推荐我们使用字面量/直接量的方式来创建字符串,并且会缓存所有以字面量形式创建的字符串对象到常量池中,当使用相同字面量再次创建字符串时会重用对象以减少内存开销,避免内存中堆积大量内容相同的字符串对象
/* 使用字面量创建字符串时: 1.JVM会检查常量池中是否有该对象: 1)若没有,则创建该字符串对象并存入常量池 2)若有,则直接将该对象返回而不再创建一个新的字符串对象 */ /* String s1 = "123abc"; //常量池还没有,因此创建该字符串对象,并存入常量池 String s2 = "123abc"; //常量池已有了,直接重用对象 String s3 = "123abc"; //常量池已有了,直接重用对象 //引用类型==,比较地址是否相同 System.out.println(s1==s2); //true System.out.println(s1==s3); //true System.out.println(s2==s3); //true */ /* 常见面试题: String s = new String("hello"); 问:创建了几个对象? 答:2个 第一个:字面量"hello" ---java会创建一个String对象表示字面量"hello",并将其存入常量池 第二个:new String() ---new String()时会再创建一个字符串,并引用hello字符串的内容 */ /* String s1 = new String("hello"); //s1装的是new String()对象的地址 String s2 = "hello"; //s2装的是字面量"hello"的地址 System.out.println("s1:"+s1); //hello System.out.println("s2:"+s2); //hello System.out.println(s1==s2); //false,因为s1与s2的地址不同 //字符串实际开发中比较相等的需求都是比较字符串的内容 //因此我们应该使用字符串提供的equals()方法来比较两个字符串的内容 System.out.println(s1.equals(s2)); //true,因为s1与s2的内容相同 */ /* String s1 = "123abc"; String s2 = "123abc"; System.out.println(s1==s2); //true,s1与s2地址相同 s1 = s1+"!"; //创建新对象并把地址赋值给s1 System.out.println(s1==s2); //false,s1为新的对象的地址,与s2不同了 */ //如下代码:常量池中会有3个存储,一个是123abc的地址,一个是123的地址,一个是abc的地址 //一个新的对象,它的值也是123abc String s1 = "123abc"; //编译器在编译时,若发现一个计算表达式可以在编译期间确定结果, //则直接运算好并将结果保存到表达式中 相当于String s2 = "123abc"; String s2 = "123"+"abc"; System.out.println(s1==s2); //true,s1与s2共用常量池中的 String s3 = "123"; //当字符串拼接产生的内容与常量池是某内容相同时,也不会重用常量池的对象 String s4 = s3+"abc"; //创建一个新的对象存储123abc System.out.println(s4==s1); //false
-
String常用方法:
-
length():获取字符串的长度(字符个数)
String str = "我爱Java!"; int len = str.length(); //获取str的长度 System.out.println(len); //7
-
trim():去除当前字符串两边的空白字符
String str = " hello world "; System.out.println(str); // hello world str = str.trim(); //去除当前字符串两边的空白字符 System.out.println(str); //hello world
-
indexOf(String str):检索给定字符串在当前字符串的开始位置
int lastIndexOf(String str):
检索给定字符串在当前字符串中最后一次出现的位置// 0123456789012345 String str = "thinking in java"; int index = str.indexOf("in"); //检索in在字符串str中出现的开始位置 System.out.println(index); //2 index = str.indexOf("IN"); //当前字符串不包含给定内容IN,所以返回-1 System.out.println(index); //-1 index = str.indexOf("in",3); //从第4个字符开始找in第一次出现的位置 System.out.println(index); //5 index = str.lastIndexOf("in"); //找in最后一次出现的位置 System.out.println(index); //9
-
substring(int start,int end):截取当前字符串中指定范围内的字符串(含头不含尾–包含start,但不包含end)
public class SubstringDemo { public static void main(String[] args) { /* // 01234567890 String str = "www.tedu.cn"; String name = str.substring(4,8); //截取第4个到第7个----下标 System.out.println(name); //tedu name = str.substring(4); //从第4个一直截取到字符串末尾----下标 System.out.println(name); //tedu.cn */ String name = getName("www.tedu.com.cn"); System.out.println(name); //tedu String str = getName("http://www.google.com"); System.out.println(str); //google } /** * 获取给定网址中的域名 * @param line 网址 * @return 返回域名 */ public static String getName(String line){ //012345678901234 //www.tedu.com.cn 第一个点到第二个点之间的字符串 int start = line.indexOf(".")+1; //4,加1目的是为了找到点后的第一个字符的位置 int end = line.indexOf(".",start); //8,从start往后找第一个.的位置 return line.substring(start,end); } }
-
charAt():返回当前字符串指定位置上的字符
// 0123456789012345 String str = "thinking in java"; char c = str.charAt(9); //获取位置9所对应的字符 System.out.println(c); //i
-
startsWith(String str)和endsWith(String str):判断当前字符串是否是以给定的字符串开始/结尾的
String str = "thinking in java"; boolean starts = str.startsWith("think"); //判断str是否是以think开头的 System.out.println("starts:"+starts); //true boolean ends = str.endsWith(".png"); //判断str是否是以.png结尾的 System.out.println("ends:"+ends); //false
-
toUpperCase()和toLowerCase():将当前字符串中的英文部分转为全大写/全小写
String str = "我爱Java!"; String upper = str.toUpperCase(); //将str中英文部分转为全大写 System.out.println(upper); //我爱JAVA! String lower = str.toLowerCase(); //将str中英文部分转为全小写 System.out.println(lower); //我爱java!
-
valueOf():String类中提供的静态方法,将其它数据类型转换为String
int a = 123; String s1 = String.valueOf(a); //将int型变量a转换为String类型并赋值给s1 System.out.println("s1:"+s1); //123 double dou = 123.456; String s2 = String.valueOf(dou); //将double型变量dou转换为String类型并赋值给s2 System.out.println("s2:"+s2); //123.456 String s3 = a + ""; //任何内容与字符串连接结果都是字符串,效率低(下周一才能体会) System.out.println(s3); //123
-
补充:
- ASCII:美国标准编码,是美国最早的字符集,也是计算机最底层的字符集,一个字节
- GBK:国标编码,中国自己的编码,总共6万多个
- Unicode:万国码,装全世界所有符号
- UTF:在Unicode基础之上做的二次编码,里面加入了一个长度信息来标记是按一个字符解析还是按两个字符算
结论:互联网上真正使用的并不是unicode,真正传输出的是UTF这种带长度信息的编码,拿到UTF数据后再把长度去掉,还原成unicode编码。
Java API基础第二天
StringBuilder
String 类型的连接性能不好,Java提供了StringBuilder解决字符串连接性能问题。
简单理解 StringBuilder性能好!(重点!)
String s1 = "ABC";
String s2 = "def";
String s3 = s1 + s2;
字符串连接性能测试:
String str = "";
long t1 = System.currentTimeMillis();
for(int i=0; i<50000; i++){
str = str + "A";
}
long t2 = System.currentTimeMillis();
System.out.pritnln(t2 - t1);
StringBuilder 用于提升String字符串的连接性
- StringBuilder称为可变字符串
- StringBuilder内部也是字符数组, 其API可以直接修改其内部数组的内容
- 当数组容量不足时候, 会自动扩容
- 运算期间会尽力减少创建数组的数量。
package string;
public class StringBuilderDemo03 {
public static void main(String[] args) {
/*
* 测试StringBuilder的连接性能
*/
StringBuilder buf = new StringBuilder();
long t1 = System.currentTimeMillis();
for (int i =0; i<50000; i++){
buf.append("A");
}
long t2 = System.currentTimeMillis();
System.out.println(t2-t1);
}
}
StringBuilder API
buf-> char[]{
A, C, B, 0, 0, 0, 0, 0, 0, 0, 0...0}
// 0 1 2 3 4 5
StringBuilder buf = new StringBuilder();
buf.append("A")
.append("A")
.append("A")
.append("B")
.insert(1,"C")
.delete(2,4);
Stirng str = buf.toString();
- append() 追加, 在StringBuilder的后面添加字符,当容量满了,会自动扩容, 扩容规则 1倍+2;
- insert(位置,字符) 插入字符;
- delete(开始位置, 结束位置): 删除一定范围的字符,包括开始,不包括结束
- StringBuilder的API返回的大多是当前对象,可以连续使用.调用方法。
- toString() 方法可以讲StringBuilder转换为String
正则表达式
用于检测、测试字符串规则的表达式.
经常用于检测字符串是否符合特定的规则,在网站上经常用于检测用户输入数据是否符合规范:
- 检测 用户名 是否为 8~10 数字 英文(大小写)
- 检测 电话号码是否符合规则
- 检测 邮箱地址是否符合规则
- 等
正则HelloWorld
最简单的正则表达式:“HelloWorld” 表示
-
一共有10个字符
-
出现的顺序必须是 HelloWorld
-
Java 提供了正则API, 用于检测一个字符串是否符合,正则规则
- boolean matchs(正则) 检测当前字符串是否符合正则规则
正则规则 rule = "HelloWorld"
字符串: s1 = "HelloKitty";
字符串: s2 = "HelloWorld";
// s1 s2 中那个字符串符合 rule 约定的规则?
boolean b1 = s1.matches(rule); //false
boolean b2 = s2.matches(rule); //true
package string;
public class RegDemo05 {
public static void main(String[] args) {
/*
* 测试正则表达式
*/
//定义正则表达式
String rule = "HelloWorld";
//定义被检测的字符串
String s1 = "HelloKitty";
String s2 = "HelloWorld";
//检测 s1 是否符合规则
boolean b1 = s1.matches(rule);
//检测 s2 是否符合规则
boolean b2 = s2.matches(rule);
System.out.println(b1);
System.out.println(b2);
}
}
字符集
匹配一个有效字符范围。
语法:
[123456]
意义:
- 匹配一个字符
- 其有效范围: 1 2 3 4 5 6 中的某一个
正则规则例子:
Hello[123456]
- 匹配6个字符
- 前5个必须是Hello
- 第6个字符,必须 1 2 3 4 5 6 中的一个
如, 可以匹配的字符串:
- “Hello1”
- “Hello2”
- “Hello3”
- …
- “Hello6”
- “Hello7” 不可以匹配!
- “HelloA” 不可以
正则例子: 我[草去艹]
字符范围
规则 | 正则表达式 | 范围 |
---|---|---|
匹配 0~9 一个字符 | [0123456789] | [0-9] |
匹配A-Z一个字符 | [ABCDEFGHIJKLMNOPQRSTUVWXYZ] | [A-Z] |
匹配a-z一个字符 | … | [a-z] |
匹配a-zA-Z一个字符 | [a-zA-Z] |
栗子:
Hello[1-6]
预定义字符集
规则 | 正则 | 预定义字符集 | 栗子 |
---|---|---|---|
匹配一个数字 | [0-9] | \d | Hello\d |
匹配一个单词字符 | [a-zA-Z0-9_] | \w | A\w |
匹配一个空白字符 | \s | Hello\sWorld | |
匹配任意一个字符 | . | A. | |
匹配一个非数字 | \D | ||
匹配一个非空白 | \S | ||
匹配一个非单词字符 | \W |
栗子, 网站上规则 用户名规则是6个单词字符:
正则规则: \w\w\w\w\w\w
java String: "\\w\\w\\w\\w\\w\\w"
测试案例:
package string;
public class RegDemo07 {
public static void main(String[] args) {
/*
* 测试 用户名规则:6个单词字符组成
* - \ 在java字符串中需要进行转义为 \\
*/
//正则表达式:
String reg = "\\w\\w\\w\\w\\w\\w";
System.out.println(reg);
//被检查的字符串
String s1 = "Jerry1"; //可以通过检查
String s2 = "Tom-12"; //不可以通过检查
String s3 = "Andy"; //不可以通过检查
System.out.println(s1.matches(reg));
System.out.println(s2.matches(reg));
System.out.println(s3.matches(reg));
}
}
数量词
约定左侧元素出现的次数。
栗子:
\w\w\w\w\w\w 等价 \w{
6}
语法:
X{n} 规定左侧X出现n次
X{n,m} 规定左侧X出现最少n次, 最多m次
X{0,n} 规定左侧X出现0到n次
X{n,} 规定左侧X出现最少n次
X? 和 X{0,1} 等价,X可以没有或者有一个
X+ 和 X{1,} 等价,X至少有一个,多了随意,简称:一个以上
X* 和 X{0,} 等价,X至少有0个,多了随意 简称:0个以上
栗子:
- 网站的用户名是 8~16个单词字符: \w{8,16}
- 网站的密码是单词字符, 最少8个, 多了不限: \w{8,}
- 匹配Hello World,中间至少有一个空白: Hello\s+World
- 不能匹配 : “HelloWorld”
- 不能匹配: “Hello World!”
- 能匹配: “Hello World”
- 能匹配: “Hello World”
- 能匹配: “Hello World”
特殊字符转义
如何匹配字符 [ ] ? + * . , 使用 \特殊字符, 进行转义!
\. 匹配点
\[ 匹配 [
\? 匹配 ?
\* 匹配 *
\+ 匹配 +
\\ 匹配 \
...
如下正则的意义:匹配 www.tedu.cn
域名
-
www.tedu.cn 匹配:
-
www.tedu.cn 通过
-
wwwAteduAcn 通过
-
www-tedu-cn 通过
-
www\.tedu\.cn
匹配- www.tedu.cn 通过
- wwwAteduAcn 不通过
- www-tedu-cn 不通过
案例:如何检查一个字符串是否为正确的IPv4地址
正确IP:
“192.168.1.25” “192.168.199.1” “10.0.0.20” “8.8.8.8”
错误的IP:
“10-10-10-20” “192点168点5点25”
正则:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
分组
讲一组规则作为整体进行处理
栗子正则:
-
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
-
(\d{1,3}\.)(\d{1,3}\.)(\d{1,3}\.)\d{1,3}
-
(\d{1,3}\.){3}\d{1,3}
package string;
public class RegDemo11 {
public static void main(String[] args) {
/*
* 检查IP地址是否符合规则
*/
//定义正则规则
//String reg = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}";
String reg = "\\d{1,3}(\\.\\d{1,3})(\\.\\d{1,3})(\\.\\d{1,3})";
//String reg = "(\\d{1,3}\\.){3}\\d{1,3}"; //测试分组
//定义被检查的字符串
String ip1 = "192.168.2.70";
String ip2 = "10.0.0.20";
String ip3 = "8.8.8.8";
//定义错误的被检查字符串
String ip4 = "192点168点2点70";
String ip5 = "192-168-2-70";
//检查
System.out.println(ip1.matches(reg));
System.out.println(ip2.matches(reg));
System.out.println(ip3.matches(reg));
System.out.println(ip4.matches(reg));
System.out.println(ip5.matches(reg));
}
}
栗子2:
-
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
-
\d{1,3}(\.\d{1,3})(\.\d{1,3})(\.\d{1,3})
-
\d{1,3}(\.\d{1,3}){3}
区别:
(\d{1,3}\.){3}\d{1,3} (分组){3} 分组的整体出现3次
\d{1,3}\.{3}\d{1,3} \.{3} .必须出现2次,可以匹配 “192...168”
java 正则API
- matches 检查字符串是否整体符合正则表达式规则
- split 劈开
- replaceAll 全部替换
Split 劈开字符串(重要)
将一个字符串劈开为几个子字符串:
- “192.168.5.140” 劈开为 “192” “168” “5” “140”
- “1, Tom, 110, tom@tedu.cn” 劈开为 “1” “Tom” “110” “tom@tedu.cn”
使用:
str 存储的是被劈开的字符串
正则 用于匹配劈开的位置点, 如: , 或者 \.
返回值 是劈开以后的数组,每个元素是 劈开的子字符串段落
劈开以后,匹配的位置就没有了
String[] arr = str.split(正则);
案例:
String str = "1, Tom, 110, tom@tedu.cn";
// , , ,
// arr= "1" " Tom" " 110" " tom@tedu.cn"
String[] arr = str.split(",");
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
/*
劈开ID地址
*/
String ip="192.168.5.25";
String[]arr=ip.split("\\.");//一定要注意和"."的区别
for (int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
replaceAll
replace: 替换
all:全部
将正则表达式匹配到的字符,都替换为新字符串
例子:
我草疫情又严重了,我去,又要做核算了。
需要替换为 ***疫情又严重了,***,又要做核算了。
代码:
Scanner scanner = new Scanner(System.in);
System.out.print("请输入:");
String str = scanner.nextLine();
//String str = "我草疫情又严重了,我去,又要做核算了。";
// str.replaceAll("正则", 替换字符串);
String s = str.replaceAll("我[去草靠艹]", "***");
System.out.println(s);
Object
包装类
二进制
什么是2进制
逢2进1的计数规则(重要)
2进制
规则:逢2进1
数字:0 1
权:128 64 32 16 8 4 2 1
基数:2
10进制计数规则
10进制
规则:逢10进1
数字:0 1 2 3 4 5 6 7 8 9
权:万 千 百 十 个
基数:10
计算机为啥是2进制?便宜!!!成本优势明显!!!
如何将2进制转换为10进制:将1位置对应权值累加求和
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001 = 1
00000000 00000000 00000000 00000010 = 2
00000000 00000000 00000000 00000011 = 2+1 = 3
00000000 00000000 00000000 00000100 = 4
00000000 00000000 00000000 00000101 = 4+1 = 5
00000000 00000000 00000000 00000110 = 4+2 = 6
00000000 00000000 00000000 00000111 = 4+2+1=7
00000000 00000000 00000000 00001000 = 8
00000000 00000000 00000000 00001001 = 8+1=9
00000000 00000000 00000000 00001010 = 8+2=10
00000000 00000000 00000000 00001011 = 8+2+1=11
00000000 00000000 00000000 00001100
00000000 00000000 00000000 00001101
00000000 00000000 00000000 00001110
00000000 00000000 00000000 00001111
00000000 00000000 00000000 00010000
...
00000000 00000000 00000000 00011001 = 16+8+1=25
...
00000000 00000000 00000000 01101000 = 64+32+8=104
...
package binary;
public class Demo01 {
public static void main(String[] args) {
/*
* 如何查看整数的2进制存储情况
* - java 编译时候,将数字编译为2进制数字
* - 运行期间变量中存储的是2进制数
* - 输出变量时候,Java利用API方法,将2进制转换为10进制字符串
* 利用valueOf方法转换!
* - Integer.toBinaryString(n) 将整数n在内存中2进制情况显示出来
*/
int n = 50; //n=110010
System.out.println(n); //利用valueOf转换2进制为10进制字符串输出
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(104));
/*
* 输出0~200的2进制, 手工计算20个数的10进制值,编程验证
*/
for(int i=0; i<200; i++){
System.out.println(Integer.toBinaryString(i));
}
}
}
什么是16进制
逢16进1的计数规则。
16进制用途:16进制用于缩写2进制。
- 2进制书写非常繁琐
- 16进制的基数是2进制的基数4次方, 2进制每4位数可以缩写为1个16进制数。
package binary;
public class Demo02 {
public static void main(String[] args) {
/*
* 2进制与16进制
* - Java7 提供了2进制字面量前缀 0b
* 可以在数字中添加下划线,不影响数值
* - 2进制直接书写非常繁琐
* - 16进制缩写2进制就非常方便
* 从2进制的最低位开始, 每4位数缩写为1位16进制数
* - 0x是16进制的前缀
* - 计算内部没有10进制,没有16进制,只有2进制!
*/
int n = 0b11_0010;//32+16+2=50
System.out.println(n);
n = 0b0001_1001_1111_0001_0100_0011_1111_0101;
// 1 9 f 1 4 3 f 5
System.out.println(Integer.toBinaryString(n));
n = 0x19f143f5;
System.out.println(Integer.toBinaryString(n));
long l = 0b10111111111111111111111111111111111111L;
}
}
补码
计算中一种表示有符号数编码,其核心思想就是将固定位数2进制分一般作为负数。
如何将固定位数2进制分一半作为负数?
- 以4位2进制数讲解如何设计补码
- 计算时候保持4位不变, 多余位自动溢出
- 最高位称为符号位,0是正数,1是负数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qLaTMLQh-1650595231644)(image-20220125153208373.png)]
package binary;
public class Demo03 {
public static void main(String[] args) {
/*
* 补码
* max 最大
* value 值
* Integer 整数
*/
int n = -3;
System.out.println(Integer.toBinaryString(n));
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
System.out.println(max);
System.out.println(min);
System.out.println(Integer.toBinaryString(max));
System.out.println(Integer.toBinaryString(min));
System.out.println(Integer.toBinaryString(-1));
System.out.println(Long.toBinaryString(-1L));
}
}
手工计算负数的值: 计算这个数比-1少0位置的权。
11111111111111111111111111111111 = -1
11111111111111111111111111111101 = -1-2=-3
11111111111111111111111111111001 = -1-2-4=-7
11111111111111111111111111111000 = -1-1-2-4=-8
11111111111111111111111101101011 = -1-4-16-128=-149
11111111111111111111111101101100 = -1-1-2-16-128=-148
package binary;
public class Demo04 {
public static void main(String[] args) {
/*
* 负数的编码
* 实验: 输出-200到0的2进制编码(补码)
* 随机选取20个数,手动计算10进制值
* 利用Java程序验算计算结果。
*/
for(int i=-200; i<0; i++){
System.out.println(Integer.toBinaryString(i));
}
}
}
long类型负数补码
1111111111111111111111111111111111111111111111111111111111111111 = -1
1111111111111111111111111111111111111111111111111111111111100110 =-26
互补对称现象:-n = ~n+1
-7 = 11111111 11111111 11111111 11111001 = -1-2-4=-7
~-7 = 00000000 00000000 00000000 00000110 = 2+4 = 6
~-7+1 = 00000000 00000000 00000000 00000111 = 1+2+4=7
54 =00000000 00000000 00000000 00110110= 2+4+16+32=54
~54 =11111111 11111111 11111111 11001001=-1-2-4-16-32=-55
~54+1 =11111111 11111111 11111111 11001010=-1-1-4-16-32=-54
代码:
public class Demo05 {
public static void main(String[] args) {
/*
* 验证补码的互补对称现象 -n=~n+1
*/
System.out.println(54);
System.out.println(Integer.toBinaryString(54));
System.out.println(~54);
System.out.println(Integer.toBinaryString(~54));
System.out.println(~54+1);
System.out.println(Integer.toBinaryString(~54+1));
}
}
2进制运算
运算符号:
~ 取反
& 与
| 或运算
>>> 右移位运算
>> 数学右移位运算
<< 左移位运算
&
与运算
运算规则:逻辑乘法 有0则0
0 & 0 -> 0
0 & 1 -> 0
1 & 0 -> 0
1 & 1 -> 1
运算时候将两个2进制数对其位,对应位置进行与运算
栗子:
1 7 9 d 5 d 9 e
n = 00010111 10011101 01011101 10011110
m = 00000000 00000000 00000000 11111111 8位掩码
k=n&m 00000000 00000000 00000000 10011110
如上运算的意义:k中存储的是n的最后8位数,如上运算叫掩码(mask)运算。m称为mask(掩码),一般从低位开始1的个数称为掩码的位数。
代码:
int n = 0x179d5d9e;
int m = 0xff;
int k = n & m;
package binary;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
/*
* 掩码运算
*/
int n = 0x179d5d9e;
//4位掩码:0xf 0b1111 15
//6位掩码:0x3f 0b111111 63
//8位掩码:0xff 0b11111111 255
int m = 0xff; //4位 6位 8位 16位
int k = n & m;
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(m));
System.out.println(Integer.toBinaryString(k));
}
}
>>>
右移位运算
运算规则, 将2进制数整体向右移动,低位自动溢出舍弃,高位补0
n = 01100111 11010111 10001111 01101101
m=n>>>1 001100111 11010111 10001111 0110110
k=n>>>2 0001100111 11010111 10001111 011011
g=n>>>8 00000000 01100111 11010111 10001111
b3 = (n>>>8) & 0xff;
代码:
int n = 0x67d78f6d;
int m = n>>>1;
int k = n>>>2;
int g = n>>>8;
int b3 = (n>>>8) & 0xff;
//按照2进制输出 n m k g b3
| 或运算
基本运算规则:逻辑加法, 有1则1
0 | 0 -> 0
0 | 1 -> 1
1 | 0 -> 1
1 | 1 -> 1
运算时候两个2进制数对齐位,对应位进行或运算
栗子:
n = 00000000 00000000 00000000 11011101
m = 00000000 00000000 10011101 00000000
k=n|m 00000000 00000000 10011101 11011101
上述计算的意义: 两数错位合并
代码:
int n = 0xdd;
int m = 0x9d00;
int k = n | m;
//检查 n m k 的2进制
<<
左移位运算
2进制数字整体向左移动,高位自动溢出,低位补0
栗子:
n = 00100000 11101111 00110101 10010000
m=n<<1 0100000 11101111 00110101 100100000
k=n<<2 100000 11101111 00110101 1001000000
g=n<<8 11101111 00110101 10010000 00000000
代码:
int n = 0x20ef3590;
int m = n<<1;
int k = n<<2;
int g = n<<8;
//按照2进制输出 n m k g
移位运算的数学意义
栗子:
16 8 4 2 1
1 0 1 = 5
1 0 1 = 10 向左移动1位 *2
1 0 1 = 20 向左移动2位 *2*2
代码:
int n = 5;
System.out.println(n<<1); //10
System.out.println(n<<2); //20
System.out.println(n<<3); //40
//...
>>>
和 >>
的区别
>>>
逻辑右移位:数字向右移动,低位自动溢出,高位补0, 结果没有数学意义。如果仅仅将数位向右移动,不考虑数学意义,则使用>>>
>>
数学右移位:数学向右移动,低位自动溢出,正数高位补0,负数高位补1, 移动一次数学除以2,小方向取整数。如果是替代数学 /2, 使用数学右移位。
栗子, 使用负数比较运算结果:
n = 11111111 11111111 11111111 11001100=-1-1-2-16-32=-52
m=n>>1 111111111 11111111 11111111 1100110=-1-1-8-16=-26
k=n>>2 1111111111 11111111 11111111 110011=-1-4-8=-13
g=n>>3 11111111111 11111111 11111111 11001=-1-2-4=-7
n>>>1 011111111 11111111 11111111 1100110=max-25没有数学意义
程序:
int n = -52; //0xffffffcc;
int m = n>>1;
int k = n>>2;
int g = n>>3;
int x = n>>>1;
//输出n m k g x
将一个整数拆分为4个字节
栗子
b1 b2 b3 b4
n = 00010111 10011101 01011101 10011110
b1 = 00000000 00000000 00000000 00010111
b2 = 00000000 00000000 00000000 10011101
b3 = 00000000 00000000 00000000 01011101
b4 = 00000000 00000000 00000000 10011110
代码:n =-1 n=-3 n=max n=min
int n = 0x179d5d9e;
int b1 = (n >>> 24) & 0xff;
int b2 = (n >>> 16) & 0xff;
int b3 = (n >>> 8) & 0xff;
int b4 = n & 0xff;
//验证:按照二进制输出 n b1 b2 b3 b4
//n=-1 时候按照10进制输出是啥结果?
将4个字节合并为一个整数
b1 = 00000000 00000000 00000000 00010111
b2 = 00000000 00000000 00000000 10011101
b3 = 00000000 00000000 00000000 01011101
b4 = 00000000 00000000 00000000 10011110
b1<<24 00010111 00000000 00000000 00000000
b2<<16 00000000 10011101 00000000 00000000
b3<<8 00000000 00000000 01011101 00000000
b4 00000000 00000000 00000000 10011110
n = (b1<<24) | (b2<<16) | (b3<<8) | b4;
代码:
int b1 = 0x17;
int b2 = 0x9d;
int b3 = 0x5d;
int b4 = 0x9e;
int n = (b1<<24) | (b2<<16) | (b3<<8) | b4;
//按照2进制输出 b1 b2 b3 b3 n
Object类
Object是所有类的顶级超类,其中有两个经常被子类重写的方法:
toString()与equals().
import java.util.Objects;
/**
* 使用当前类学习常备子类重写的Object中的相关方法
*/
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
package object;
/**
* Object是所有类的顶级超类.里面有几个经常被子类重写的方法,其中包括toString和equals
*/
public class Demo {
public static void main(String[] args) {
Point p = new Point(1,2);
/*
Object已经实现了toString方法.直接继承下来时返回的字符串内容为当前对象的地址信息.格式为:类名@地址.
toString方法实际开发中很少直接写代码去调用它,都是在不经意间被自动执行的.
例如在使用System.out.println输出时.与字符串连接操作时.
*/
//System.out.println(Object obj)输出给定对象toString返回的字符串到控制台
System.out.println(p);
//字符串连接操作时,会将非String类型的对象调用toString转换为字符串后拼接.
String line = "这是一个Point:" + p;
System.out.println(line);
Point p2 = new Point(1,2);
System.out.println("p2:"+p2);
/*
对于引用类型而言,变量保存的值是对象的地址.
==比较是比较两个变量的值是否相等,因此对于引用类型而言就是比较地址是否相等,那么意思就是比较是否为同一个对象.
equals是Object定义的另一个方法,意图是比较两个对象的内容是否相同.但是如果子类不重写这个方法,则没有实际意义,因为Object实现时内部仍然是用==比较的!
*/
System.out.println(p == p2);//false
System.out.println(p.equals(p2));//true
}
}
包装类
java定义了8个包装类,目的是为了解决基本类型不能直接参与面向对象开发的问题,使得基本类型可以通过包装类的实例以对象的形式存在.
- 其中数字类型的包装类都继承自java.lang.Number,而char和boolean的包装类直接继承自Object
- Number是一个抽象类,定义了一些方法,目的是让包装类可以将其表示的基本类型转换为其他数字类型.
package integer;
public class IntegerDemo1 {
public static void main(String[] args) {
//基本类型转换为包装类
int i = 123;
//java推荐我们使用包装类的静态方法valueOf将基本类型转换为包装类,而不是直接new
Integer i1 = Integer.valueOf(i);//Integer会重用-128-127之间的整数对象
Integer i2 = Integer.valueOf(i);
System.out.println(i1==i2);//true
System.out.println(i1.equals(i2));//true
double dou = 123.123;
Double dou1 = Double.valueOf(dou);//Double则是直接new
Double dou2 = Double.valueOf(dou);
System.out.println(dou1==dou2);//false
System.out.println(dou1.equals(dou2));//true
//包装类转换为基本类型
int in = i1.intValue();//获取包装类对象中表示的基本类型值
double doub = i1.doubleValue();
System.out.println(in);//123
System.out.println(doub);//123.0
in = dou1.intValue();//大类型转小类型可能存在丢精度!
doub = dou1.doubleValue();
System.out.println(in);//123
System.out.println(doub);//123.123
}
}
包装类常用功能
package integer;
public class IntegerDemo2 {
public static void main(String[] args) {
//1可以通过包装类获取其表示的基本类型的取值范围
//获取int的最大值和最小值?
int imax = Integer.MAX_VALUE;
System.out.println(imax);
int imin = Integer.MIN_VALUE;
System.out.println(imin);
long lmax = Long.MAX_VALUE;
System.out.println(lmax);
long lmin = Long.MIN_VALUE;
System.out.println(lmin);
/*
2字符串转换为基本类型的前提是该字符串正确描述了基本类型可以保存的值,否则
会抛出异常:NumberFormatException
*/
String str = "123";
// String str = "123.123";//这个字符串不能解析为int值!
int d = Integer.parseInt(str);
System.out.println(d);//123
double dou = Double.parseDouble(str);
System.out.println(dou);//123.123
}
}
自动拆装箱特性
JDK5之后推出了一个新的特性:自动拆装箱
该特性是编译器认可的.当编译器编译源代码时发现有基本类型和引用类型相互赋值使用时会自动补充代码来完成他们的转换工作,这个过程称为自动拆装箱.
package integer;
public class IntegerDemo3 {
public static void main(String[] args) {
/*
触发自动拆箱特性,编译器会补充代码将包装类转换为基本类型,下面的代码会变为:
int i = new Integer(123).intValue();
*/
int i = new Integer(123);
/*
触发编译器自动装箱特性,代码会被编译器改为:
Integer in = Integer.valueOf(123);
*/
Integer in = 123;
}
}
File类
File类的每一个实例可以表示硬盘(文件系统)中的一个文件或目录(实际上表示的是一个抽象路径)
使用File可以做到:
- 1:访问其表示的文件或目录的属性信息,例如:名字,大小,修改时间等等
- 2:创建和删除文件或目录
- 3:访问一个目录中的子项
但是File不能访问文件数据.
public class FileDemo {
public static void main(String[] args) {
//使用File访问当前项目目录下的demo.txt文件
/*
创建File时要指定路径,而路径通常使用相对路径。
相对路径的好处在于有良好的跨平台性。
"./"是相对路径中使用最多的,表示"当前目录",而当前目录是哪里
取决于程序运行环境而定,在idea中运行java程序时,这里指定的
当前目录就是当前程序所在的项目目录。
*/
// File file = new File("c:/xxx/xxx/xx/xxx.txt");
File file = new File("./demo.txt");
//获取名字
String name = file.getName();
System.out.println(name);
//获取文件大小(单位是字节)
long len = file.length();
System.out.println(len+"字节");
//是否可读可写
boolean cr = file.canRead();
boolean cw = file.canWrite();
System.out.println("是否可读:"+cr);
System.out.println("是否可写:"+cw);
//是否隐藏
boolean ih = file.isHidden();
System.out.println("是否隐藏:"+ih);
}
}
创建一个新文件
createNewFile()方法,可以创建一个新文件
package file;
import java.io.File;
import java.io.IOException;
/**
* 使用File创建一个新文件
*/
public class CreateNewFileDemo {
public static void main(String[] args) throws IOException {
//在当前目录下新建一个文件:test.txt
File file = new File("./test.txt");
//boolean exists()判断当前File表示的位置是否已经实际存在该文件或目录
if(file.exists()){
System.out.println("该文件已存在!");
}else{
file.createNewFile();//将File表示的文件创建出来
System.out.println("文件已创建!");
}
}
}
删除一个文件
delete()方法可以将File表示的文件删除
package file;
import java.io.File;
/**
* 使用File删除一个文件
*/
public class DeleteFileDemo {
public static void main(String[] args) {
//将当前目录下的test.txt文件删除
/*
相对路径中"./"可以忽略不写,默认就是从当前目录开始的。
*/
File file = new File("test.txt");
if(file.exists()){
file.delete();
System.out.println("文件已删除!");
}else{
System.out.println("文件不存在!");
}
}
}
创建目录
mkDir():创建当前File表示的目录
mkDirs():创建当前File表示的目录,同时将所有不存在的父目录一同创建
package file;
import java.io.File;
/**
* 使用File创建目录
*/
public class MkDirDemo {
public static void main(String[] args) {
//在当前目录下新建一个目录:demo
// File dir = new File("demo");
File dir = new File("./a/b/c/d/e/f");
if(dir.exists()){
System.out.println("该目录已存在!");
}else{
// dir.mkdir();//创建目录时要求所在的目录必须存在
dir.mkdirs();//创建目录时会将路径上所有不存在的目录一同创建
System.out.println("目录已创建!");
}
}
}
删除目录
delete()方法可以删除一个目录,但是只能删除空目录。
package file;
import java.io.File;
/**
* 删除一个目录
*/
public class DeleteDirDemo {
public static void main(String[] args) {
//将当前目录下的demo目录删除
File dir = new File("demo");
// File dir = new File("a");
if(dir.exists()){
dir.delete();//delete方法删除目录时只能删除空目录
System.out.println("目录已删除!");
}else{
System.out.println("目录不存在!");
}
}
}
访问一个目录中的所有子项
listFiles方法可以访问一个目录中的所有子项
package file;
import java.io.File;
/**
* 访问一个目录中的所有子项
*/
public class ListFilesDemo1 {
public static void main(String[] args) {
//获取当前目录中的所有子项
File dir = new File(".");
/*
boolean isFile()
判断当前File表示的是否为一个文件
boolean isDirectory()
判断当前File表示的是否为一个目录
*/
if(dir.isDirectory()){
/*
File[] listFiles()
将当前目录中的所有子项返回。返回的数组中每个File实例表示其中的一个子项
*/
File[] subs = dir.listFiles();
System.out.println("当前目录包含"+subs.length+"个子项");
for(int i=0;i<subs.length;i++){
File sub = subs[i];
System.out.println(sub.getName());
}
}
}
}
获取目录中符合特定条件的子项
重载的listFiles方法:File[] listFiles(FileFilter)
该方法要求传入一个文件过滤器,并仅将满足该过滤器要求的子项返回。
package file;
import java.io.File;
import java.io.FileFilter;
/**
* 重载的listFiles方法,允许我们传入一个文件过滤器从而可以有条件的获取一个目录
* 中的子项。
*/
public class ListFilesDemo2 {
public static void main(String[] args) {
/*
需求:获取当前目录中所有名字以"."开始的子项
*/
File dir = new File(".");
if(dir.isDirectory()){
// FileFilter filter = new FileFilter(){//匿名内部类创建过滤器
// public boolean accept(File file) {
// String name = file.getName();
// boolean starts = name.startsWith(".");//名字是否以"."开始
// System.out.println("过滤器过滤:"+name+",是否符合要求:"+starts);
// return starts;
// }
// };
// File[] subs = dir.listFiles(filter);//方法内部会调用accept方法
File[] subs = dir.listFiles(new FileFilter(){
public boolean accept(File file) {
return file.getName().startsWith(".");
}
});
System.out.println(subs.length);
}
}
}
使用递归操作删除一个目录
循环是重复执行某个步骤,而递归是重复整个过程。
package file;
import java.io.File;
/**
* 编写一个程序,要求实现1+2+3+4+....100并输出结果。
* 代码中不能出现for,while关键字
*
* 编写程序计算:
* 一个人买汽水,1块钱1瓶汽水。3个瓶盖可以换一瓶汽水,2个空瓶可以换一瓶汽水。不考虑赊账问题
* 问20块钱可以最终得到多少瓶汽水。
*
* 删除一个目录
*/
public class Test {
public static void main(String[] args) {
File dir = new File("./a");
delete(dir);
}
/**
* 将给定的File对象表示的文件或目录删除
* @param file
*/
public static void delete(File file){
if(file.isDirectory()) {
//清空目录
File[] subs = file.listFiles();
for (int i = 0; i < subs.length; i++) {
File sub = subs[i];//从目录中获取一个子项
//将该子项删除
delete(sub);//递归调用
}
}
file.delete();
}
}
Lambda表达式
JDK8之后,java支持了lambda表达式这个特性.
- lambda可以用更精简的代码创建匿名内部类.但是该匿名内部类实现的接口只能有一个抽象方法,否则无法使用!
- lambda表达式是编译器认可的,最终会将其改为内部类编译到class文件中
package lambda;
import java.io