洋蔥

贪婪,找不到比这更好的词了,是件好事。

推荐:

正则表达式 - 维基百科

正则表达式 - 菜鸟

Java正则表达式 - 菜鸟

重用正则总结

1
([a-zA-Z -]*)([0-9]*)    # 利用捕获组匹配,可匹配字符串示例:"abc90"、"90",第一组为字符串(空或abc),第二组为数字(90)。

正则表达式

定义

  • 正则表达式定义了字符串的模式。
  • 正则表达式可以用来搜索、编辑或处理文本。
  • 正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。

转义

  • 在 Java 中,\\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊意义。
  • 在其他语言中(如Perl),一个反斜杠 \ 就足以具有转义的作用,而在 Java 中则需要有两个反斜杠才能被解析为其他语言中的转义作用。
  • Java 的正则表达式中,两个 \\ 代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\\\

常用元字符

^$*+?[a-z]\w\W{n, m}

常见规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
A:字符
x 字符 x。 举例:'a'表示字符a
\\ 反斜线字符。 两个杠才能表示一个杠,杠具有转义作用。
\n 新行(换行)符 ('\u000A')
\r 回车符 ('\u000D')
B:字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a到 z 或 A到 Z,两头的字母包括在内(范围)
[0-9] 0到9的字符都包括
C:预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)。'.' 字符本身,怎么表示呢? \.
\d 数字:[0-9]
\w 单词字符:[a-zA-Z_0-9]
D:边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
不是单词字符的地方就是单词边界。
举例:hello world?haha;xixi 有三个单词边界
E:Greedy 数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次

String类

String 类中有几个可以使用正则的方法,实际都是通过调用 Pattern、Matcher 类实现的

1
2
3
4
5
6
7
8
9
10
// 判断功能
public boolean matches(String regex)

// 替换功能
public String replaceFirst(String regex, String replacement)
public String replaceAll(String regex, String replacement)

// 分割功能
public String[] split(String regex)
public String[] split(String regex, int limit)

Pattern和Matcher类

Pattern 类:正则表达式的编译表示,没有公共构造方法。

使用方法:正则表达式字符串先被编译为此类的实例,然后用得到的 Pattern 对象创建 Matcher 对象。执行 matcher 方法后的所有匹配都驻留在匹配器 Matcher 中,所以多个匹配器可以共享同一模式。

1
2
3
4
public static Pattern compile(String regex) // 将给定的正则表达式编译成一个模式。
public static Pattern compile(String regex, int flags) // 将给定的正则表达式编译成具有给定标志的模式。
public Matcher matcher(CharSequence input) // 创建一个匹配器,将给定输入与此模式匹配。
public static boolean matches(String regex, CharSequence input) // 编译给定的正则表达式并尝试匹配给定的输入。

Matcher 类:没有公共构造方法。

通过解释 Pattern 对字符序列执行匹配操作的引擎

匹配器通过调用 Pattern 的 matcher 方法创建一个 Matcher 对象。创建后,Matcher 可用于执行三种不同类型的匹配操作:

  • matches 方法尝试将整个输入序列与模式匹配。
  • lookingAt 方法尝试将输入序列与模式匹配,从头开始。
  • find 方法扫描输入序列以查找与模式匹配的下一个子序列。

每个方法都返回一个表示成功或失败的布尔值。通过查询匹配器的状态可以获取关于成功匹配的更多信息。

如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。

1
2
3
public boolean matches():尝试将整个区域与模式匹配。
public boolean find():尝试查找与该模式匹配的输入序列的下一个子序列。 如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。
public String group():返回由以前匹配操作所匹配的输入子序列。以前匹配操作所匹配的字符串形式的子序列(可能为空)。

PatternSyntaxException 类:一个非强制异常类,它表示一个正则表达式模式中的语法错误。

具体使用说明及更多方法见 API。

典型调用顺序

1
2
3
4
5
6
7
8
Pattern p = Pattern.compile("a*b"); // 正则表达式字符串先被编译为Pattern实例。
Matcher m = p.matcher("aaaaab"); // 用得到的Pattern对象创建Matcher对象,所有状态都驻留在匹配器Matcher中。多个匹配器可以共享同一模式。
boolean b = m.matches();

Pattern.compile(regex).matcher(input).matches()

// 等同于
boolean b = Pattern.matches("a*b", "aaaaab")

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* 获取功能
* Pattern和Matcher类的使用
* 模式和匹配器的基本使用顺序
*/
public class RegexDemo {
public static void main(String[] args) {
// 模式和匹配器的典型调用顺序
Pattern p = Pattern.compile("a*b");// 先吧正则表达式字符串编译为Pattern实例。
Matcher m = p.matcher("aaaaab");// 用得到的Pattern对象创建Matcher对象,所有状态都驻留在匹配器Matcher中。
boolean b = m.matches(); // 调用匹配器对象的功能
// boolean b = Pattern.compile("a*b").matcher("aaaaab").matches(); // 链式调用 Pattern.compile(regex).matcher(input).matches()
// boolean b = Pattern.matches("a*b", "aaaaab"); // 简化调用 Pattern.matches(regex, input)
System.out.println(b); // true

boolean bb = p.matcher("aaaaa").matches();// 多个匹配器可以共享同一模式。
System.out.println(bb); // false

// 用String类
String str = "aaaaab";
String regex = "a*b";
boolean cc = str.matches(regex); // 与上面的"链式调用"和"简化调用"行为相同
System.out.println(cc);// true
}
}

捕获组

  • 捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。
  • 捕获组是通过从左至右计算其开括号来编号。例如,在表达式 ((A)(B(C))),有四个这样的组:((A)(B(C)))(A)(B(C))(C)
  • Matcher 类的 groupCount 方法返回一个 int 值,表示 Matcher 对象有多个捕获组。
  • group(0) 是一个特殊的组,代表整个表达式。该组不包括在 groupCount 的返回值中。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RegexDemo {
public static void main(String[] args) {
String line = "This order was placed for QT3000! OK?";
Pattern pattern = Pattern.compile("(\\D*)(\\d+)(.*)");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
System.out.println("Found value: " + matcher.group(0));
System.out.println("Found value: " + matcher.group(1));
System.out.println("Found value: " + matcher.group(2));
System.out.println("Found value: " + matcher.group(3));
} else {
System.out.println("NO MATCH");
}
}
}

输出:

1
2
3
4
Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT
Found value: 3000
Found value: ! OK?

正则注入(regex injection)

定义

攻击者可能会通过恶意构造的输入对初始化的正则表达式进行修改,比如导致正则表达式不符合程序规定要求;可能会影响控制流,导致信息泄露,或导致ReDos攻击。

避免使用不可信数据构造正则表达式。

利用方式

  • 匹配标志:不可信的输入可能覆盖匹配选项,然后有可能会被传给 Pattern.compile() 方法。
  • 贪赞:一个非受信的输入可能试图注入一个正则表达式,通过它来改变初始的那个正则表达式,从而匹配尽可能多的字符串,从而暴露敏感信息。
  • 分组:程序员会用括号包括一部分的正则表达式以完成一组动作中某些共同的部分。攻击者可能通过提供非受信的输入来改变这种分组。

输入校验

  • 非受信的输入应该在使用前净化,从而防止发生正则表达式注入。
  • 当用户必须指定正则表达式作为输入时,必须注意需要保证初始的正则表达式没有被无限制修改。
  • 在用户输入字符串提交给正则解析之前,进行白名单字符处理 (比如字母和数字)。
  • 开发人员必须仅仅提供有限的正则表达式功能给用户,从而减少被误用的可能。

ReDos攻击

正则表达式拒绝服务( ReDoS ) 是一种算法复杂性攻击,它通过提供 正则表达式需要很长时间评估的输入 来产生拒绝服务(即:通过提供特制的正则表达式或输入来使程序花费大量时间,消耗系统资源,然后程序将变慢或变得无响应)。

ReDos攻击概述

  • JDK 中提供的正则匹配使用的是 NFA 引擎。
  • NFA 引擎具有回溯机制(一个字符可能尝试多次匹配),匹配失败时花费时间很大。 正则表达式回溯法原理
  • 当使用简单的非分组正则表达式时,是不会导致ReDos攻击的。

潜在危险

  • 包含具有自我重复的重复性分组的正则
    举例:^(\d+)+$^(\d*)*$^(\d+)*$^(\d+|\s+)*$
  • 包含替换的重复性分组
    举例:^(\d|\d|\d)+$^(\d|\d?)+$

当输入字符串为 1111111111111111111111x1 时,正则表达式 ^(\d+)+$ 就会不断进行失败重试,从而耗死CPU计算。

解析: \d+ 表示匹配一个或多个数字; ()+ 表示分组本身也匹配一个或多个;那么匹配字符串 1111111111111111111111x1 就会进行非常多的尝试,从而导致CPU资源枯竭。

规避猎施

  • 进行正则匹配前,先对匹配的文本的长度进行校验
  • 在编写正则时,尽量不要使用过于复杂的正则,越复杂越容易有缺陷。
  • 在编写正则时,尽量减少分组的使用。
  • 避免动态构建正则(因为难以判断是否有性能问题),当使用不可信数据构造正则时,要使用白名单进行严格校验。

案例

校验qq号码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* 校验qq号码.
* 1:要求必须是5-15位数字
* 2:0不能开头
* 分析:
* A:键盘录入一个QQ号码
* B:写一个功能实现校验
* C:调用功能,输出结果。
*/
public class RegexDemo {
public static void main(String[] args) {
// 创建键盘录入对象
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的QQ号码:");
String qq = sc.nextLine();
System.out.println("checkQQ:" + checkQQ(qq));
}

/*
* 写一个功能实现校验两个明确:明确返回值类型:boolean 明确参数列表:String qq
*/
public static boolean checkQQ(String qq) {
boolean flag = true;
// 校验长度
if (qq.length() >= 5 && qq.length() <= 15) {
// 0不能开头
if (!qq.startsWith("0")) {
// 必须是数字
char[] chs = qq.toCharArray();
for (int x = 0; x < chs.length; x++) {
char ch = chs[x];
if (!Character.isDigit(ch)) {
flag = false;
break;
}
}
} else {
flag = false;
}
} else {
flag = false;
}
return flag;
}
}

用正则表达式改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RegexDemo {
public static void main(String[] args) {
// 创建键盘录入对象
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的QQ号码:");
String qq = sc.nextLine();
System.out.println("checkQQ:" + checkQQ(qq));
}

public static boolean checkQQ(String qq) {
// String类的 public boolean matches(String regex)告知此字符串是否匹配给定的正则表达式
// return qq.matches("[1-9][0-9]{4,14}");
return qq.matches("[1-9]\\d{4,14}");
}
}

校验电话号码和邮箱

img

按照不同的规则分割数据

一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
* 分割功能
* String类的public String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
* 举例:
* 百合网,世纪佳缘,珍爱网,QQ
* 搜索好友
* 性别:女
* 范围:"18-24"
* age>=18 && age<=24
*/
public class RegexDemo {
public static void main(String[] args) {
// 定义一个年龄搜索范围
String ages = "18-24";
// 定义规则
String regex = "-";
// 调用方法
String[] strArray = ages.split(regex);
// //遍历
for (int x = 0; x < strArray.length; x++) {
System.out.println(strArray[x]);
}
// 如何得到int类型的呢?
int startAge = Integer.parseInt(strArray[0]);
int endAge = Integer.parseInt(strArray[1]);
// 键盘录入年龄
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的年龄:");
int age = sc.nextInt();
if (age >= startAge && age <= endAge) {
System.out.println("你就是我想找的");
} else {
System.out.println("不符合我的要求,gun");
}
}
}

二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
* 分割功能练习
*/
public class RegexDemo {
public static void main(String[] args) {
// 定义一个字符串
String s1 = "aa,bb,cc";
// 直接分割
String[] str1Array = s1.split(",");
for (int x = 0; x < str1Array.length; x++) {
System.out.println(str1Array[x]);
}
String s2 = "aa.bb.cc";
String[] str2Array = s2.split("\\.");
for (int x = 0; x < str2Array.length; x++) {
System.out.println(str2Array[x]);
}
String s3 = "aa bb cc";
String[] str3Array = s3.split(" +");
for (int x = 0; x < str3Array.length; x++) {
System.out.println(str3Array[x]);
}
// 硬盘上的路径,我们应该用\\替代\
String s4 = "E:\\JavaSE\\day14\\avi";
String[] str4Array = s4.split("\\\\");// 两个杠代表一个杠
for (int x = 0; x < str4Array.length; x++) {
System.out.println(str4Array[x]);
}
}
}

三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
* 我有如下一个字符串:"91 27 46 38 50"
* 请写代码实现最终输出结果是:"27 38 46 50 91"
* 分析:
* A:定义一个字符串
* B:把字符串进行分割,得到一个字符串数组
* C:把字符串数组变换成int数组
* D:对int数组排序
* E:把排序后的int数组在组装成一个字符串
* F:输出字符串
*/
public class RegexDemo {
public static void main(String[] args) {
String s = "91 27 46 38 50";
String[] strArray = s.split(" ");
int[] arr = new int[strArray.length];
for (int x = 0; x < arr.length; x++) {
arr[x] = Integer.parseInt(strArray[x]);
}
Arrays.sort(arr);
// String result1 = Arrays.toString(arr);
// System.out.println("result1:"+result1);// result1:[27, 38, 46, 50, 91]
StringBuilder sb = new StringBuilder();
for (int x = 0; x < arr.length; x++) {
sb.append(arr[x]).append(" ");
}
String result2 = sb.toString().trim();
System.out.println("result2:" + result2);// result2:27 38 46 50 91
}
}

把论坛中的数字替换为*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 替换功能
* String类的public String replaceAll(String regex,String replacement)
* 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
*/
public class RegexDemo {
public static void main(String[] args) {
String str = "hello!qq:12345;world!kh:622112345678;java!";
// 我要去除所有的数字,用*给替换掉
String regex1 = "\\d+";
String ss1 = "*";
String result1 = str.replaceAll(regex1, ss1);
System.out.println(result1); // hello!qq:*;world!kh:*;java!
String regex2 = "\\d";
String ss2 = "*";
String result2 = str.replaceAll(regex2, ss2);
System.out.println(result2); // hello!qq:*****;world!kh:************;java!
// 直接把数字干掉
String regex3 = "\\d+";
String ss3 = "";
String result3 = str.replaceAll(regex3, ss3);
System.out.println(result3); // hello!qq:;world!kh:;java!
}
}

获取字符串中由3个字符组成的单词

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* 获取功能:
* 获取下面这个字符串中由三个字符组成的单词
* da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu?
*/
public class RegexDemo {
public static void main(String[] args) {
String s = "da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu?";
String regex = "\\b\\w{3}\\b";// 正则表达式
Pattern p = Pattern.compile(regex);// 把正则表达式编译成模式对象
Matcher m = p.matcher(s);// 通过模式对象得到匹配器对象
// 调用匹配器对象的功能,通过find方法就是查找有没有满足条件的子串:public boolean find()
while (m.find()) {
// 如何得到值呢? public String group()
System.out.println(m.group());
}
// 注意:一定要先find(),然后才能group()
}
}

常用集合结构

线性结构

  • List:ArrayList、CopyOnWriteArrayListVector
  • Queue: LinkedList、PriorityQueueBlockingQueueConcurrentLinkedQueue
  • Stack:extends from Vector

非线性结构

  • Set:HashSet、LinkedHashSet、TreeSet、CopyOnWriteArraySetConcurrentSkipListSet
  • Map:HashMap、TreeMap、WeakHashMap、IdentityHashMap、 LinkedHashMap 、 HashTableConcurrentHashMapConcurrentSkipListMap

集合包装类

集合包装类可以对上述的几种结构进行包装,而它在其中的返回会加入它的语义理解

  • 不可变更集合类:Collections.unmodifiableCollection,对上述结构的限定,返回不可修改的集合
  • 同步集合类Collections.synchronizedCollection,可以将任意List转换成线程安全的List
  • 可检查集合类:Collections.checkedCollection

注:其中 斜体 标注的类为线程安全类。

Java集合框架图

线性存储结构

重点选讲以 List 展开的类

ArrayList

  • 可设置初始的空间大小,在底层数组不够用时在原来的基础上扩容1.5倍

Vector

  • 可设置初始的空间大小,在底层数组不够用时在原来的基础上默认扩容2倍 (亦可设置增长的空间大小)
  • 读写方法都加了synchronized关键字,是线程安全的

CopyOnWriteArrayList

  • 利用写时复制的思想,一旦碰到写操作都会对所有元素进行复制
  • 在新的数组上修改后,重新对数组赋值引用,保证线程安全
  • 适合读多写少的应用场景

ConcurrentLinkedQueue

  • 非阻塞式队列,利用CAS算法保证入队出队线程安全
  • 进行元素遍历时是线程不安全
  • 不允许添加null元素

映射关系

WeakHashMap

  • 适用于需要缓存的场景,对entry进行弱引用管理
  • GC时会自动释放弱引用的entry项
  • 相对HashMap只增加弱引用管理,并不保证线程安全

HashTable

  • 读写方法都加了synchronized关键字
  • key和value都不允许出现null值
  • 与HashMap不同的是HashTable直接使用对象的hashCode,不会重新计算hash值

ConcurrentHashMap

  • 利用CAS+ Synchronized来确保线程安全
  • 底层数据结构依然是数组+链表+红黑树
  • 对哈希项进行分段上锁,效率上较之HashTable要高
  • key和value都不允许出现null值

问题:

为什么ConcurrentHashMap不允许插入Null值?

小测

ArrayList list = new ArrayList(20); 中的 List 扩充了几次?

初始化时直接分配大小,不存在扩充情况,所以是0次;add等操作时才可能会发生扩充。

Math类

​ 包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。

1、针对数学运算进行操作的类

2、常见方法(更多方法见API)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  成员变量:
    public static final double PI
    public static final double E

  成员方法:
    public static int abs(int a):绝对值
    public static double ceil(double a):向上取整
    public static double floor(double a):向下取整
    public static int max(int a,int b):最大值
    public static int min(int a, int b):最小值
    public static double pow(double a,double b):a的b次幂
    public static double random():随机数 [0.0,1.0)
    public static int round(float a):四舍五入。
      返回最接近参数的 int。结果将舍入为整数:加上 1/2,对结果调用 floor 并将所得结果强制转换为 int 类型。换句话说,结果等于以下表达式的值:
        (int)Math.floor(a + 0.5f)
    public static long round(double a):四舍五入。
      返回最接近参数的 long。结果将舍入为整数:加上 1/2,对结果调用 floor 并将所得结果强制转换为 long 类型。换句话说,结果等于以下表达式的值:
        (long)Math.floor(a + 0.5d)
    public static double sqrt(double a):正平方根,返回正确舍入的 double 值的正平方根

  System.out.println("max:" + Math.max(Math.max(12, 23), 18)); // 方法的嵌套调用

案例:

  A:猜数字小游戏

  B:获取任意范围的随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
* 需求:请设计一个方法,可以实现获取任意范围内的随机数。
* 分析:
* A:键盘录入两个数据。
* int strat;
* int end;
* B:想办法获取在start到end之间的随机数
* 我写一个功能实现这个效果,得到一个随机数。(int)
* C:输出这个随机数
*/
public class MathDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入开始数:");
int start = sc.nextInt();
System.out.println("请输入结束数:");
int end = sc.nextInt();
for (int x = 0; x < 100; x++) {
// 调用功能
int num = getRandom(start, end);
// 输出结果
System.out.println(num);
}
}

// 写一个功能两个明确:返回值类型:int 参数列表:int start,int end
public static int getRandom(int start, int end) {
// 1-100之间的随机数:int number = (int) (Math.random() * 100) + 1;
int number1 = (int) (Math.random() * end) + start;// 错误,获得的是 [start, end + start)范围的随机数
int number2 = (int) (Math.random() * (end - start + 1)) + start;// 有问题, 当 start > end 时, 如 start=100,end=95,获取的是[97, 100]范围的随机数
int number3 = (int) (Math.random() * (Math.abs(end - start) + 1)) + Math.min(start, end); // 正确
return number3;
}
}

Random类

1.用于产生随机数的类

  如果用相同的种子创建两个 Random 实例,则对每个实例进行相同的方法调用序列,它们将生成并返回相同的数字序列。

2.构造方法:

  A:Random():创建一个新的随机数生成器。此构造方法将随机数生成器的种子设置为某个值,该值与此构造方法的所有其他调用所用的值完全不同。

        没有给种子,用的是默认种子,是当前时间的毫秒值,每次产生的随机数不同

  B:Random(long seed): 使用单个 long 种子创建一个新的随机数生成器。该种子是伪随机数生成器的内部状态的初始值,该生成器可通过方法 protected int next(int bits) 维护。

              指定种子,每次种子相同,随机数就相同

3.成员方法:

A:int nextInt() 返回int范围内的随机数

1
2
3
4
// nextInt方法源码
public int nextInt() {
return next(32);
}

B:int nextInt(int n) 返回[0,n)范围内的随机数

1
2
3
4
5
6
7
8
9
10
11
12
public class RandomDemo {
public static void main(String[] args) {
// 创建对象
// Random r = new Random();// 未给定种子,每次产生的随机数不同
Random r = new Random(1111);// 给定种子,每次产生的随机数相同
for (int x = 0; x < 10; x++) {
// int num = r.nextInt();// int范围内的随机数
int num = r.nextInt(100) + 1;// [1,100]内的随机数
System.out.println(num);
}
}
}

System类

1.System 类包含一些有用的 字段和方法。System 类不能被实例化。

  在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。

  解析:说明字段和方法都是跟类相关的,跟类相关不就是静态的么,说明没有构造方法

2.成员方法(更多方法见API)

1
2
3
4
  A: public static void gc():运行垃圾回收器
  B: public static void exit(int status):终止当前正在运行的 Java 虚拟机。参数用作状态码;根据惯例,非 0 的状态码表示异常终止。
  C: public static long currentTimeMillis():获取当前时间的毫秒值
  D: public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length):从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。

案例:

  A. 运行垃圾回收器

    System.gc()可用于垃圾回收。当使用System.gc()回收某个对象所占用的内存之前,通过要求程序调用适当的方法来清理资源。在没有明确指定资源清理的情况下,Java提高了默认机制来清理该对象的资源,也就是调用Object类的finalize()方法。finalize()方法的作用是释放一个对象占用的内存空间时,会被JVM调用。而子类重写该方法,就可以清理对象占用的资源,该方法没有链式调用,所以必须手动实现。

    从程序的运行结果可以发现,执行System.gc()前,系统会自动调用finalize()方法清除对象占有的资源,通过super.finalize()方式可以实现从下到上的finalize()方法的调用,即先释放自己的资源,再去释放父类的资源。

    但是,不要在程序中频繁的调用垃圾回收,因为每一次执行垃圾回收,jvm都会强制启动垃圾回收器运行,这会耗费更多的系统资源,会与正常的Java程序运行争抢资源,只有在执行大量的对象的释放,才调用垃圾回收最好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class RandomDemo {
public static void main(String[] args) {
Person p = new Person("林俊杰", 60);
System.out.println(p);
p = null; // 让p不再指定堆内存
System.gc();
}
}

class Person {
@Override
protected void finalize() throws Throwable {
System.out.println("当前的对象被回收了" + this);
super.finalize();
}

private String name;
private int age;

public Person() {
super();
}

public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

  B. 退出jvm

    public static void exit(int status)

      终止当前正在运行的 Java 虚拟机。参数用作状态码;根据惯例,非 0 的状态码表示异常终止。

      该方法调用 Runtime 类中的 exit 方法。该方法永远不会正常返回。

      调用 System.exit(n) 实际上等效于调用:

        Runtime.getRuntime().exit(n)

  C. 获取当前时间的毫秒值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SystemDemo {
public static void main(String[] args) {
System.out.println("我们喜欢林青霞(东方不败)");
// System.exit(0); // 后面代码将终止执行
System.out.println("我们也喜欢赵雅芝(白娘子)");
// System.out.println(System.currentTimeMillis());// 单独得到这样的实际目前对我们来说意义不大,那么,它到底有什么作用呢?

// 要求:请大家给我统计这段程序的运行时间
long start = System.currentTimeMillis();
for (int x = 0; x < 100000; x++) {
System.out.println("hello" + x);
}
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "毫秒");
}
}

  D. 数组复制

1
2
3
4
5
6
7
8
9
public class SystemDemo {
public static void main(String[] args) {
int[] arr1 = { 11, 22, 33, 44, 55 };
int[] arr2 = { 6, 7, 8, 9, 10 };
System.arraycopy(arr1, 1, arr2, 2, 2);
System.out.println(Arrays.toString(arr1));// [11, 22, 33, 44, 55]
System.out.println(Arrays.toString(arr2));// [6, 7, 22, 33, 10]
}
}

BigInteger类

大数字

  a. java.math.BigInteger:整数

  b. java.math.BigDecimal:浮点数

1、针对大整数的运算(BigInteger:可以让超过Integer范围内的数据进行运算)

2、构造方法

  A:BigInteger(String s)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BigIntegerDemo {
public static void main(String[] args) {
// 超过int范围内,Integer就不能再表示,所以就更谈不上计算了。
Integer i = new Integer(100);
System.out.println(i); // 100
System.out.println(Integer.MAX_VALUE); // 2147483647
Integer ii = new Integer("2147483647");
System.out.println(ii); // 2147483647
// Integer iii = new Integer("2147483648"); // NumberFormatException
// System.out.println(iii);
// 通过大整数来创建对象
BigInteger bi = new BigInteger("2147483648");
System.out.println("bi:" + bi); // bi:2147483648
}
}

3、成员方法(更多方法见API)

  A: public BigInteger add(BigInteger val):加

  B: public BigInteger subtract(BigInteger val):减

  C: public BigInteger multiply(BigInteger val):乘

  D: public BigInteger divide(BigInteger val):除

  E: public BigInteger[] divideAndRemainder(BigInteger val):返回商和余数的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BigIntegerDemo {
public static void main(String[] args) {
BigInteger bi1 = new BigInteger("100");
BigInteger bi2 = new BigInteger("50");
System.out.println("add:" + bi1.add(bi2)); // add:150
System.out.println("subtract:" + bi1.subtract(bi2)); // subtract:50
System.out.println("multiply:" + bi1.multiply(bi2)); // multiply:5000
System.out.println("divide:" + bi1.divide(bi2)); // divide:2
BigInteger[] bis = bi1.divideAndRemainder(bi2);
System.out.println("商:" + bis[0]); // 商:2
System.out.println("余数:" + bis[1]); // 余数:0
}
}

BigDecimal类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
* 看程序写结果:结果和我们想的有一点点不一样,这是因为float类型的数据存储和整数不一样导致的。它们大部分的时候,都是带有有效数字位。
* 由于在运算的时候,float类型和double很容易丢失精度,演示案例。所以,为了能精确的表示、计算浮点数,Java提供了BigDecimal
* BigDecimal类:不可变的、任意精度的有符号十进制数,可以解决数据丢失问题。
*/
public class BigDecimalDemo {
public static void main(String[] args) {
System.out.println(0.09 + 0.01); // 0.09999999999999999
System.out.println(1.0 - 0.32); // 0.6799999999999999
System.out.println(1.015 * 100); // 101.49999999999999
System.out.println(1.301 / 100); // 0.013009999999999999
System.out.println(1.0 - 0.12); // 0.88
}
}

1、浮点数据做运算,会丢失精度。所以,针对浮点数据的操作建议采用BigDecimal。(金融相关的项目)

2、构造方法

  A:BigDecimal(String s)

3、成员方法:

  A: public BigDecimal add(BigDecimal augend):加

  B: public BigDecimal subtract(BigDecimal subtrahend):减

  C: public BigDecimal multiply(BigDecimal multiplicand):乘

  D: public BigDecimal divide(BigDecimal divisor):除

  E: public BigDecimal divide(BigDecimal divisor,int scale,int roundingMode):商,几位小数,如何舍取

    divisor - 此 BigDecimal 要除以的值。

    scale - 要返回的 BigDecimal 商的标度。

    roundingMode - 要应用的舍入模式。

  F: public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode):商,几位小数,如何舍取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class BigDecimalDemo {
public static void main(String[] args) {
System.out.println(0.09 + 0.01); // 0.09999999999999999
System.out.println(1.0 - 0.32); // 0.6799999999999999
System.out.println(1.015 * 100); // 101.49999999999999
System.out.println(1.301 / 100); // 0.013009999999999999

BigDecimal bd1 = new BigDecimal("0.09");
BigDecimal bd2 = new BigDecimal("0.01");
System.out.println("add:" + bd1.add(bd2)); // add:0.10

BigDecimal bd3 = new BigDecimal("1.0");
BigDecimal bd4 = new BigDecimal("0.32");
System.out.println("subtract:" + bd3.subtract(bd4)); // subtract:0.68

BigDecimal bd5 = new BigDecimal("1.015");
BigDecimal bd6 = new BigDecimal("100");
System.out.println("multiply:" + bd5.multiply(bd6)); // multiply:101.500

BigDecimal bd7 = new BigDecimal("1.301");
BigDecimal bd8 = new BigDecimal("100");
System.out.println("divide:" + bd7.divide(bd8)); // divide:0.01301
System.out.println("divide:" + bd7.divide(bd8, 3, BigDecimal.ROUND_HALF_UP)); // divide:0.013
System.out.println("divide:" + bd7.divide(bd8, 3, 4)); // divide:0.013
System.out.println("divide:" + bd7.divide(bd8, 8, BigDecimal.ROUND_HALF_UP)); // divide:0.01301000

System.out.println("divide:" + bd7.divide(bd8, 3, RoundingMode.HALF_UP)); // divide:0.013
}
}

日期相关类

  A. java.util.Date类:最常用的功能为获得系统的当前时间。

  B. java.util.Calendar类:与日历相关的类。可以获得日期相关的信息,还可以进行日期的计算。

  C. java.text.DateFormat及其子类:可以对String与Date进行相互的转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*********************************************************************/
Date date = new Date();
System.out.println(date); // Sat Apr 01 11:32:05 CST 2017
// 将Date转换为String(格式化)
DateFormat df = DateFormat.getDateInstance();
String s = df.format(date);
System.out.println(s); // 2017-4-1
// 将String转换为Date(解析)
s = "2008-8-8";
try {
date = df.parse(s);
System.out.println(date); // Fri Aug 08 00:00:00 CST 2008
} catch (ParseException e) {
System.out.println("日期不合法!!!");
}
/*********************************************************************/
Date date = new Date();
System.out.println(date);// Sat Apr 01 11:38:09 CST 2017
// 将Date转换为String(格式化)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(date);
System.out.println(s);// 2017-04-01 11:38:09
// 将String转换为Date(解析)
s = "2008-8-8 8:8:8";
try {
date = sdf.parse(s);
System.out.println(date);// Fri Aug 08 08:08:08 CST 2008
} catch (ParseException e) {
System.out.println("日期不合法!!!");
}
/*********************************************************************/

Date

类 Date 表示特定的瞬间,精确到毫秒。

  A:构造方法

    public Date():创建自 1970 年 1 月 1 日 00:00:00 GMT 到当前的默认毫秒值的日期对象

    public Date(long time):创建自 1970 年 1 月 1 日 00:00:00 GMT 到给定的毫秒值的日期对象

1
2
3
4
5
6
7
8
9
10
11
public class DateDemo {
public static void main(String[] args) {
Date d1 = new Date();
System.out.println("d1:" + d1); // d:Sat Apr 01 09:10:13 CST 2017
// long time = System.currentTimeMillis();
long time = 1000 * 60 * 60; // 1小时
Date d2 = new Date(time);
System.out.println("d2:" + d2); // d2:Thu Jan 01 09:00:00 CST 1970
// 不是 01:00:00 是因为我们使用的是北京时间,北京时间是中国采用北京东八时区的区时作为标准时间
}
}

  B:成员方法

    public long getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

    public void setTime(long time):设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 以后 time 毫秒的时间点。

1
2
3
4
5
6
7
8
9
10
11
public class DateDemo {
public static void main(String[] args) {
Date d = new Date();
long time = d.getTime();
System.out.println(time); // 1491009637592
System.out.println(System.currentTimeMillis()); // 1491009637592
System.out.println("d:" + d); // d:Sat Apr 01 09:20:37 CST 2017
d.setTime(1000);
System.out.println("d:" + d); // d:Thu Jan 01 08:00:01 CST 1970
}
}

  C:日期和毫秒值的相互转换

    案例:你来到这个世界多少天了?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
* 算一下你来到这个世界多少年?
* 分析:
* A:键盘录入你的出生的年月日
* B:把该字符串转换为一个日期
* C:通过该日期得到一个毫秒值
* D:获取当前时间的毫秒值
* E:用D-C得到一个毫秒值
* F:把E的毫秒值转换为年
* /1000/60/60/24/365
*/
public class MyYearOldDemo {
public static void main(String[] args) throws ParseException {
// 键盘录入你的出生的年月日
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的出生年月日(格式为yyyy-MM-dd):");
String line = sc.nextLine();
// 把该字符串转换为一个日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(line);
// 通过该日期得到一个毫秒值
long myTime = d.getTime();
// 获取当前时间的毫秒值
long nowTime = System.currentTimeMillis();
// 用D-C得到一个毫秒值
long time = nowTime - myTime;
// 把E的毫秒值转换为年
long day = time / 1000 / 60 / 60 / 24 / 365;
System.out.println("你来到这个世界:" + day + "年");
}
}

DateFormat

针对日期进行格式化和针对字符串进行解析的类,但是是抽象类,所以使用其子类SimpleDateFormat

  A: SimpleDateFormat的构造方法:

    public SimpleDateFormat():用默认的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。

    public SimpleDateFormat(String pattern): 用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。

      这个模式字符串该如何写呢? 通过查看API,我们就找到了对应的模式

        y 年

        M 年中的月份

        d 月份中的天数

        H 一天中的小时数(0-23)

        m 小时中的分钟数

        s 分钟中的秒数

      yyyy-MM-dd HH:mm:ss 2017-05-01 12:12:12

      yyyy年MM月dd日 HH:mm:ss 2017年05月01日 12:12:12

  B:日期和字符串的转换

    a:Date – String(格式化)

      public final String format(Date date)

    b:String – Date(解析)

      public Date parse(String source)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DateFormatDemo {
public static void main(String[] args) throws ParseException {
Date d = new Date();
// 创建格式化对象
SimpleDateFormat sdf1 = new SimpleDateFormat();
String s1 = sdf1.format(d);
System.out.println(s1); // 17-5-1 上午9:50

// 给定模式
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
// DateFormat类的 public final String format(Date date)
String s2 = sdf2.format(d);
System.out.println(s2); // 2017年05月01日 09:52:15

// String -- Date
String str = "2008-08-08 12:12:12";
// 在把一个字符串解析为日期的时候,请注意格式必须和给定的字符串格式匹配
SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dd = sdf3.parse(str);
System.out.println(dd); // Fri Aug 08 12:12:12 CST 2008
}
}

  C:制作了一个针对日期操作的工具类。

img

日期类的时间从为什么是从1970年1月1日

I suspect that Java was born and raised on a UNIX system.
UNIX considers the epoch (when did time begin) to be midnight, January 1, 1970.
是说java起源于UNIX系统,而UNIX认为1970年1月1日0点是时间纪元.

但这依然没很好的解释”为什么”,出于好奇,继续Google,总算找到了答案:

http://en.wikipedia.org/wiki/Unix_time

这里的解释是:

最初计算机操作系统是32位,而时间也是用32位表示。
System.out.println(Integer.MAX_VALUE);
2147483647
Integer在JAVA内用32位表 示,因此32位能表示的最大值是2147483647。
另外1年365天的总秒数是31536000,
2147483647/31536000 = 68.1

也就是说32位能表示的最长时间是68年,而实际上到2038年01月19日03时14分07
秒,便会到达最大时间,过了这个时间点,所有32位操作系统时间便会变为
10000000 00000000 00000000 00000000

也就是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。

到这里,我想问题的答案已经出来了:

因为用32位来表示时间的最大间隔是68年,而最早出现的UNIX操作系统考虑到计算
机产生的年代和应用的时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始
时间),而java自然也遵循了这一约束。

至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作
系统可以表示到292,277,026,596年12月4日15时30分08秒,相信我们的N代子孙,哪
怕地球毁灭那天都不用愁不够用了,因为这个时间已经是千亿年以后了。

最后一个问题:上面System.out.println(new Date(0)),打印出来的时间是8点而非0点,
原因是存在系统时间和本地时间的问题,其实系统时间依然是0点,只不过我的电脑时区
设置为东8区,故打印的结果是8点。

Calendar类

它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段 之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。

1、日历类,封装了所有的日历字段值,通过统一的方法根据传入不同的日历字段可以获取值。

2、如何得到一个日历对象呢?

  Calendar rightNow = Calendar.getInstance();// 使用默认时区和语言环境获得一个日历。返回的 Calendar 基于当前时间,使用了默认时区和默认语言环境。

  本质返回的是子类对象

3、成员方法

  A: public int get(int field):返回给定日历字段的值。日历类中的每个日历字段都是静态的成员变量,并且是int类型

  B: public void add(int field,int amount): 根据日历的规则,为给定的日历字段添加或减去指定的时间量。

  C: public final void set(int year,int month,int date): 设置日历字段 YEAR、MONTH 和 DAY_OF_MONTH 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CalendarDemo {
public static void main(String[] args) {
// 其日历字段已由当前日期和时间初始化:
Calendar c = Calendar.getInstance();
// 获取年
int year = c.get(Calendar.YEAR);
// 获取月
int month = c.get(Calendar.MONTH);
// 获取日
int date = c.get(Calendar.DATE);
System.out.println(year + "年" + (month + 1) + "月" + date + "日"); // 2017年4月1日
// Month 值是基于 0 的。例如,0 表示 January。
/*********************************************************************************/
// 三年前的今天
c.add(Calendar.YEAR, -3);
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH);
date = c.get(Calendar.DATE);
System.out.println(year + "年" + (month + 1) + "月" + date + "日"); // 2014年4月1日
/*********************************************************************************/
// 5年后的10天前
c.add(Calendar.YEAR, 5);
c.add(Calendar.DATE, -10);
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH);
date = c.get(Calendar.DATE);
System.out.println(year + "年" + (month + 1) + "月" + date + "日"); // 2019年3月22日
/*********************************************************************************/
c.set(2011, 11, 11);
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH);
date = c.get(Calendar.DATE);
System.out.println(year + "年" + (month + 1) + "月" + date + "日"); // 2011年12月11日
}
}
  1. 案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 计算任意一年的二月有多少天
* 分析:
* A:键盘录入任意的年份
* B:设置日历对象的年月日
* 年就是A输入的数据
* 月是2
* 日是1
* C:把时间往前推一天,就是2月的最后一天
* D:获取这一天输出即可
*/
public class CalendarDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入年份:");
int year = sc.nextInt();
// 设置日历对象的年月日
Calendar c = Calendar.getInstance();
c.set(year, 2, 1); // 其实是这一年的3月1日
c.add(Calendar.DATE, -1); // 把时间往前推一天,就是2月的最后一天
System.out.println(c.get(Calendar.DATE));
}
}

二分查找(二分法)

查找:

基本查找:数组元素无序(从头找到尾)

二分查找(折半查找):数组元素有序

数组高级二分查找原理图解:

img

代码:

img

注意:下面这种做法是有问题的。

因为数组本身是无序的,所以这种情况下的查找不能使用二分查找。

所以你先排序了,但是你排序的时候已经改变了我最原始的元素索引。

必须使用基本查找,除非需求不在意。

img

十大排序算法

包装类

定义

包装类的作用:基本类型不是类,所以无法获得类的基本特性,无法参与转型、泛型、集合、反射等过程,包装类的出现就是为了弥补这个缺陷。

为了对基本类型的数据进行更多的操作,将 基本数据类型 封装成类,并在类中定义一些功能,这个类就是 包装类(8个)

  byte Byte

  short Short

  int Integer

  long Long

  float Float

  double Double

  char Character

  boolean Boolean

常见的操作之一:用于基本数据类型与字符串之间的转换。

装箱与拆箱

装箱

​ 将 基本数据类型 转换为 包装类类型。

​ 自动装箱 基本类型 – 包装类类型(引用类型)

拆箱

​ 将 包装类类型 转换为 基本数据类型。

​ 自动拆箱 包装类类型(引用类型)– 基本类型

自动装箱与拆箱

从JDK1.5开始可以自动装箱与拆箱。所以下面是正确的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        Integer num1 = 100;
        int num2 = new Integer(100);

      把下面的这个代码理解即可:

        Integer num = 100;
        num += 200;
        System.out.println("num:" + num);

      通过*反编译*后的代码:

        Integer num = Integer.valueOf(100); // 自动装箱
        num = Integer.valueOf(num.intValue() + 200); // 自动拆箱,再自动装箱
        System.out.println((new StringBuilder("num:")).append(num).toString()); // 我们用 + 号做的拼接,其本质是调用append()方法。因为用 + 号做拼接内存占用空间比较大,字符串拼接浪费很多空间,所以在底层自动做了转换。

      需要注意:
        在使用时,Integer num = null;上面的代码就会出现NullPointerException。因为num为空引用无法调用方法。

Integer

  Integer 类在对象中包装了一个基本类型 int 的值。Integer 类型的对象包含一个 int 类型的字段。

  此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。

构造方法

1
2
3
4
5
6
7
8
9
10
11
         public Integer(int value)

        Integer i = new Integer(100);

     public Integer(String s)

        Integer i = new Integer("100");

      注意:这里的字符串必须是由数字字符组成

          抛出: NumberFormatException - 如果 String 不包含可解析的整数。

成员方法

1
2
3
4
5
public static final int MAX_VALUE
System.out.println(Integer.MAX_VALUE);// 2147483647

public static final int MIN_VALUE
System.out.println(Integer.MIN_VALUE);// -2147483648

​ A. String、包装类、基本数据类型 的相互转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        public int intValue()
        public static int parseInt(String s)
        public static String toString(int i)
        public static Integer valueOf(int i)
        public static Integer valueOf(String s)

       String – 包装类 //将String转换为包装类,使用包装类的valueOf()
          String s = "100";
          Integer num1 = Integer.valueOf(s);
          Double num2 = Double.valueOf(s);

       String – int //将String转换为基本数据类型,使用对应包装类的parseXXX()即可
          String str = "100";
        1). int num1 = new Integer(str).intValue();
        2). int num2 = Integer.parseInt(str); //推荐,*记住很重要*
        3). double num3 = Double.parseDouble(str)

       int – String
          int value = 100;
        1). String s1 = "" + value;
        2). String s2 = String.valueOf(value); //推荐,它可以将任意类型转换成字符串
        3). String s3 = new Integer(value).toString();
        4). String s4 = Integer.valueOf(value).toString();
        5). String s5 = Integer.toString(value);

​ B. 进制转换(了解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
        public static String toBinaryString(int i)
        public static String toOctalString(int i)
        public static String toHexString(int i)
        public static String toString(int i, int radix)
        public static int parseInt(String s, int radix)

       常用的基本进制转换:
        public static String toBinaryString(int i)
          System.out.println(Integer.toBinaryString(100));// 1100100
        public static String toOctalString(int i)
          System.out.println(Integer.toOctalString(100));// 144
        public static String toHexString(int i)
          System.out.println(Integer.toHexString(100));// 64

       十进制转其他进制:
        public static String toString(int i, int radix) :radix的范围(即进制的范围)236
        为什么? 0,...9,a...z 一共36
          System.out.println(Integer.toString(100, 2).toUpperCase());// 1100100
          System.out.println(Integer.toString(100, 8).toUpperCase());// 144
          System.out.println(Integer.toString(100, 16).toUpperCase());// 64
          System.out.println(Integer.toString(100, 1).toUpperCase());// 100
          System.out.println(Integer.toString(100, 37).toUpperCase());// 100

       其他进制转十进制:
        public static int parseInt(String s, int radix)
        System.out.println(Integer.parseInt("1100100", 2));// 100
        System.out.println(Integer.parseInt("144", 8));// 100
        System.out.println(Integer.parseInt("64", 16));// 100

        System.out.println(Integer.parseInt("100", 10));// 100
        System.out.println(Integer.parseInt("100", 2));// 4
        System.out.println(Integer.parseInt("100", 8));// 64
        System.out.println(Integer.parseInt("100", 16));// 256

        System.out.println(Integer.parseInt("123", 2)); //抛出:NumberFormatException - 如果 String 不包含可解析的 int。

案例

​ -128到127之间的数据缓冲池问题

​ 注意:Integer的数据直接赋值,如果在-128到127之间,会直接从缓冲池里获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class IntegerDemo {
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2); // false
System.out.println(i1.equals(i2)); // true
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4); // false
System.out.println(i3.equals(i4)); // true
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i5 == i6); // false
System.out.println(i5.equals(i6)); // true
Integer i7 = 127;
Integer i8 = 127;
System.out.println(i7 == i8); // true
System.out.println(i7.equals(i8)); // true
// 通过查看源码,我们就知道了,针对-128到127之间的数据,做了一个数据缓冲池,如果数据是该范围内的,每次并不创建新的空间
// Integer ii = Integer.valueOf(127);
}
}

Character(了解)

​ Character 类在对象中包装一个基本类型 char 的值。Character 类型的对象包含类型为 char 的单个字段。

​ 此外,该类提供了几种方法,以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,反之亦然。

​ 字符信息基于 Unicode 标准,版本 4.0。

构造方法

1
2
3
4
public Character(char value)
Character ch = new Character((char) 97);
Character ch = new Character('a');
System.out.println("ch:" + ch);//ch:a -- 说明重写了toString()方法。

成员方法

1
2
3
4
5
A.    public static boolean isUpperCase(char ch):    判断给定的字符是否是大写
B. public static boolean isLowerCase(char ch): 判断给定的字符是否是小写
C. public static boolean isDigit(char ch): 判断给定的字符是否是数字字符
D. public static char toUpperCase(char ch): 把给定的字符转换为大写
E. public static char toLowerCase(char ch): 把给定的字符转换为小写

案例

​ 统计字符串中大写,小写及数字字符出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class IntegerDemo {
public static void main(String[] args) {
// 定义三个统计变量。
int bigCount = 0;
int smallCount = 0;
int numberCount = 0;
// 键盘录入一个字符串。
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
// 把字符串转换为字符数组。
char[] chs = line.toCharArray();
// 历字符数组获取到每一个字符
for (int x = 0; x < chs.length; x++) {
char ch = chs[x];
// 判断该字符
if (Character.isUpperCase(ch)) {
bigCount++;
} else if (Character.isLowerCase(ch)) {
smallCount++;
} else if (Character.isDigit(ch)) {
numberCount++;
}
}
// 输出结果即可
System.out.println("大写字母:" + bigCount + "个");
System.out.println("小写字母:" + smallCount + "个");
System.out.println("数字字符:" + numberCount + "个");
}
}

弊端

包装类的弊端,不断地拆装箱会引发性能问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
Integer sum1 = 1;

StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < 1000000000; i++) {
sum1 += 1; // Integer.valueOf(sum1.intValue() + i);
}
stopWatch.stop();
System.out.println(stopWatch.getTotal(TimeUnit.MILLISECONDS)); // 当 sum1 为int类型时,只需要1毫秒
System.out.println(sum1);
}
}

重载和自动拆装箱

有更匹配的重载方法时,不自动拆装箱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
fun(new Integer(3)); // Param is a Integer!
fun(3); // Param is a int!
}

static void fun(Integer i) {
System.out.println("Param is a Integer!");
}

static void fun(int i) {
System.out.println("Param is a int!");
}
}

常量池

注意:包装类的比较必须使用equals(), ‘==’ 比较的是两个引用是否指向一个对象。

包装类的创建很浪费性能,因此Java对简单的数字(-128~127)对应的包装类进行了缓存,称为常量池。通过直接量赋值的包装类如果在此范围内,会直接使用常量池中的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
public static void main(String[] args) {
Integer i1 = 200;
Integer i2 = 200;
Integer i3 = 100;
Integer i4 = 100;
Integer i5 = new Integer(100);

System.out.println(i1 == i2); // false
System.out.println(i3 == i4); // true
System.out.println(i3 == i5); // false
}
}

Object类

API 的概述:

​ 应用程序接口,包括为开发Java程序而预定义的类和接口。简而言之,就是JDK提供给我们的一些提高编程效率的java类。

阅读全文 »

类与对象

事物: 类:

   属性 成员变量

   行为 成员方法

 类:是一组相关的属性和行为的集合。是一个抽象的概念。  学生:类

对象:是该类事物的具体表现形式。具体存在的个体。     班长:对象

阅读全文 »

关键字、标识符、注释、常量

关键字

  1. 被Java语言赋予特定含义的单词

  2. 特点:组成关键字单词的字母全部小写。

  3. 注意:

    A. goto和const是保留字

    B. 类似于 Notepad++、EditPlus 这样的高级文本编辑器,针对关键字都有特殊的颜色标记。

阅读全文 »

软件、程序、计算机语言

程序

  计算机程序通常称为软件,是发给计算机的指令。程序设计就是创建一个可以让计算机执行并完成所需任务的程序。

  计算机本身的语言:机器语言,指令都是以二进制代码的形式存在。

  汇编语言:低级的程序设计语言。具有机器依赖性,只能在某种特定的机器上执行。

img

阅读全文 »
0%