【Java】Java基础

1:计算机概述(了解)

(1)计算机
(2)计算机硬件
(3)计算机软件
系统软件:window,linux,mac
应用软件:qq,yy
(4)软件开发(理解)
软件:是由数据和指令组成的。(计算器)
开发:就是把软件做出来。
如何实现软件开发呢?
就是使用开发工具和计算机语言做出东西来
(5)语言
自然语言:人与人交流沟通的
计算机语言:人与计算机交流沟通的
C,C++,C#,Java
(6)人机交换
图形界面:操作方便只管
DOS命令:需要记忆一些常见的命令

 

2:键盘功能键的认识和快捷键(掌握)

(1)功能键的认识
tab
shift
ctrl
alt
windos
空格
上下左右
回车
截图
(2)快捷键
全选 Ctrl+A
复制 Ctrl+C
粘贴 Ctrl+V
剪切 Ctrl+X
撤销 Ctrl+Z
保存 Ctrl+S

 

3:常见的DOS命令(掌握)

(1)常见的如下
盘符的切换
d:回车
目录的进入
cd javase
cd javase\code
目录的回退
cd..
cd\
清屏
cls
退出
exit
(2)其他的几个(了解)
创建目录
删除目录
创建文件
删除文件
显示目录下的内容
删除带内容的目录

 

4:Java语言概述(了解)

(1)Java语言的发展史
Java之父

JDK1.4.2
JDK5
JDK7
(2)Java语言的特点
有很多小特点,重点有两个开源,跨平台
(3)Java语言是跨平台的,请问是如何保证的呢?(理解)
我们是通过翻译的案例讲解的。

针对不同的操作系统,提高不同的jvm来实现的。
(4)Java语言的平台
JavaSE
JavaME--Android
JavaEE

 

5:JDK,JRE,JVM的作用及关系(掌握)

(1)作用
JVM:保证Java语言跨平台
JRE:Java程序的运行环境
JDK:Java程序的开发环境
(2)关系
JDK:JRE+工具
JRE:JVM+类库


 

6:JDK的下载,安装,卸载(掌握)

(1)下载到官网。
A:也可以到百度搜索即可。
B:我给你。
(2)安装
A:绿色版 解压就可以使用
B:安装版 必须一步一步的安装,一般只要会点击下一步即可

注意:
建议所有跟开发相关的软件都不要安装在有中文或者空格的目录下。
(3)卸载
A:绿色版 直接删除文件夹
B:安装版
a:控制面板 -- 添加删除程序
b:通过专业的软件卸载工具。(比如360的软件管家卸载)

 

7:第一个程序:HelloWorld案例(掌握)

class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
(1)程序解释:
A:Java程序的最基本单位是类,所以我们要定义一个类。
格式:class 类名
举例:class HelloWorld
B:在类中写内容的时候,用大括号括起来。
C:Java程序要想执行,必须有main方法。
格式:public static void main(String[] args)
D:要指向那些东西呢,也用大括号括起来。
E:你要做什么呢?今天我们仅仅做了一个简单的输出
格式:System.out.println("HelloWorld");
注意:""里面的内容是可以改动的。

(2)Java程序的开发执行流程:
A:编写java源程序(.java)
B:通过javac命令编译生成.class文件
C:通过java命令运行.class文件
 

8:常见的问题(掌握)

(1)扩展名被隐藏
如何找到:工具--文件夹选项--查看--去除隐藏扩展名的那个勾勾
(2)我要求文件名称和类名一致。
实际上不这样做也是可以的。
但是,注意:
javac后面跟的是文件名+扩展名
java后面跟的类名不带扩展名

(3)Java语言严格区分大小写,请注意。
还有就是单词不要写错了。
(4)见到非法字符: \65307肯定是中文问题。
我们写程序要求标点符号必须全部是英文状态。
(5)括号的配对问题。
一般来说,括号都是成对出现的。
(6)遇到
在类 HelloWorld 中找不到主方法, 请将主方法定义为
肯定是主方法的格式问题。

 

9:path环境变量(掌握)

(1)path环境变量的作用
保证javac命令可以在任意目录下运行。
同理可以配置qq等
(2)path配置的两种方案:
A:方案1(了解)
B:方案2
找到环境变量的位置,在系统变量里面
新建:
变量名:JAVA_HOME
变量值:D:\develop\Java\jdk1.7.0_60
修改:
变量名:Path
变量值:%JAVA_HOME%\bin;以前的内容

 

10:classpath环境变量(理解)

(1)classpath环境变量的作用
保证class文件可以在任意目录下运行
(2)classpath环境变量的配置
找到环境变量的位置,在系统变量里面
新建:
变量名:classpath

变量值:E:\JavaSE\code\HelloWorld案例

============================================================================

 

1:关键字(掌握)

(1)被Java语言赋予特定含义的单词
(2)特点:
全部小写
(3)注意事项:
A:goto和const作为保留字存在。
B:类似于Notepad++这样的高级记事本会对关键字有特殊颜色标记

 

2:标识符(掌握)

(1)就是给类,接口,方法,变量等起名字的字符序列
(2)组成规则:
A:英文大小写字母
B:数字
C:$_
(3)注意事项:
A:不能以数字开头
B:不能是java中的关键字
C:区分大小写
(4)常见的命名规则(见名知意)
A:包 全部小写
单级包:小写
举例:liuyi,com
多级包:小写,并用.隔开
举例:com.baidu
B:类或者接口
一个单词:首字母大写
举例:Student,Demo
多个单词:每个单词首字母大写
举例:HelloWorld,StudentName
C:方法或者变量
一个单词:首字母小写
举例:name,main
多个单词:从第二个单词开始,每个单词首字母大写
举例:studentAge,showAllNames()
D:常量
全部大写

一个单词:大写
举例:PI
多个单词:大写,并用_隔开
举例:STUDENT_MAX_AGE

 

3:注释(掌握)

(1)就是对程序进行解释说明的文字
(2)分类:
A:单行注释 //
B:多行注释 /**/
C:文档注释(后面讲) /** */
(3)把HelloWorld案例写了一个带注释的版本。
后面我们要写一个程序的过程。
需求:
分析:
实现:
代码体现:
(4)注释的作用
A:解释说明程序,提高了代码的阅读性。
B:可以帮助我们调试程序。
后面我们会讲解一个更高端的一个调试工具

 

4:常量(掌握)

(1)在程序执行的过程中,其值不发生改变的量
(2)分类:
A:字面值常量
B:自定义常量
(后面讲)
(3)字面值常量
A:字符串常量    "hello"
B:整数常量 12,23
C:小数常量 12.345
D:字符常量 'a','A','0'
E:布尔常量 true,false
F:空常量 null(后面讲)
(4)在Java中针对整数常量提供了四种表现形式
A:二进制         由0,1组成。以0b开头。
B:八进制         由0,1,...7组成。以0开头。
C:十进制         由0,1,...9组成。整数默认是十进制
D:十六进制     由0,1,...9,a,b,c,d,e,f(大小写均可)组成。以0x开头。

 

5:进制转换(了解)

(1)其他进制到十进制
系数:就是每一个位上的数值
基数:x进制的基数就是x
权:对每一个位上的数据,从右,并且从0开始编号,对应的编号就是该数据的权。

结果:系数*基数^权次幂之和。
(2)十进制到其他进制
除基取余,直到商为0,余数反转。
(3)进制转换的快速转换法
A:十进制和二进制间的转换
8421码。
B:二进制到八进制,十六进制的转换

 

6:变量(掌握)

(1)在程序的执行过程中,其值在某个范围内可以发生改变的量
(2)变量的定义格式:
A:数据类型 变量名 = 初始化值;
B:数据类型 变量名;
  变量名 = 初始化值;

 

7:数据类型(掌握)

(1)Java是一种强类型语言,针对每种数据都提供了对应的数据类型。
(2)分类:
A:基本数据类型:4类8种
B:引用数据类型:类,接口,数组。
(3)基本数据类型
A:整数 占用字节数
byte         1
short        2
int            4
long         8

B:浮点数
float         4
double     8
C:字符
char         2
D:布尔
boolean   1

注意:
整数默认是int类型,浮点数默认是double

长整数要加L或者l。
单精度的浮点数要加F或者f。


 

8:数据类型转换(掌握)

(1)boolean类型不参与转换
(2)默认转换
A:从小到大
B:byte,short,char -- int -- long -- float -- double
C:byte,short,char之间不相互转换直接转成int类型参与运算
(3)强制转换
A:从大到小
B:可能会有精度的损失,一般不建议这样使用。
C:格式:
目标数据类型 变量名 = (目标数据类型) (被转换的数据);
(4)思考题和面试题:
A:下面两种方式有区别吗?
float f1 = 12.345f;
float f2 = (float)12.345;
B:下面的程序有问题吗,如果有,在哪里呢?
byte b1 = 3;
byte b2 = 4;
byte b3 = b1 + b2;  // 报错。

正确写法如下:

byte b3 = (byte) (b1 + b2);

byte b4 = 3 + 4;
C:下面的操作结果是什么呢?
byte b = (byte)130; // -126
D:字符参与运算
是查找ASCII里面的值
'a' 97
'A' 65
'0' 48

System.out.println('a');
System.out.println('a' + 1);
E:字符串参与运算
这里其实是字符串的连接

System.out.println("hello"+'a'+1);
System.out.println('a'+1+"hello");
System.out.println("5+5="+5+5);

System.out.println(5+5+"=5+5");

 

============================================================================

1:运算符(掌握)

(1)算术运算符
A:+,-,*,/,%,++,--
B:+的用法
a:加法
b:正号
c:字符串连接符
C:/和%的区别
数据做除法操作的时候,/取得是商,%取得是余数
D:++和--的用法
a:他们的作用是自增或者自减
b:使用
**单独使用
放在操作数据的前面和后面效果一样。

a++或者++a效果一样。
**参与操作使用
放在操作数的前面:先自增或者自减,再参与操作

int a = 10;
int b = ++a;
放在操作数的后面:先参与操作,再自增或者自减
int a = 10;
int b = a++;
(2)赋值运算符
A:=,+=,-=,*=,/=,%=等
B:=叫做赋值运算符,也是最基本的赋值运算符
int x = 10; 把10赋值给int类型的变量x。
C:扩展的赋值运算符的特点
隐含了自动强制转换。

面试题:
short s = 1;
s = s + 1;

short s = 1;
s += 1;
请问上面的代码哪个有问题?

(3)比较运算符
A:==,!=,>,>=,<,<=
B:无论运算符两端简单还是复杂最终结果是boolean类型。
C:千万不要把==写成了=
(4)逻辑运算符
A:&,|,^,!,&&,||
B:逻辑运算符用于连接boolean类型的式子
C:结论
&:有false则false
|:有true则true
^:相同则false,不同则true。
情侣关系。
!:非true则false,非false则true

&&:结果和&是一样的,只不过有短路效果。左边是false,右边不执行。
||:结果和|是一样的,只不过有短路效果。左边是true,右边不执行

(5)位运算符(了解)
A:^的特殊用法
一个数据针对另一个数据位异或两次,该数不变
B:面试题
a:请实现两个变量的交换
**采用第三方变量
**用位异或运算符
左边a,b,a
右边a^b
b:请用最有效率的方式计算出2乘以8的结果
2<<3
(6)三元运算符
A:格式
比较表达式?表达式1:表达式2;
B:执行流程:
首先计算比较表达式的值,看是true还是false。
如果是true,表达式1就是结果。
如果是false,表达式2就是结果。
C:案例:
a:比较两个数据是否相等
b:获取两个数据中的最大值
c:获取三个数据中的最大值

2:键盘录入(掌握)

(1)实际开发中,数据是变化的,为了提高程序的灵活性,我们加入键盘录入数据。
(2)如何实现呢?目前就记住
A:导包
import java.util.Scanner;
位置:在class的上边
B:创建对象
Scanner sc = new Scanner(System.in);
C:获取数据
int x = sc.nextInt();
(3)把三元运算符的案例加入键盘录入改进。
 

3:流程控制语句

(1)顺序结构         从上往下,依次执行
(2)选择结构 按照不同的选择,执行不同的代码
(3)循环结构         做一些重复的代码
 

4:if语句(掌握)

(1)三种格式
A:格式1
if(比较表达式) {
语句体;
}

执行流程:
判断比较表达式的值,看是true还是false
如果是true,就执行语句体
如果是false,就不执行语句体

B:格式2
if(比较表达式) {
语句体1;
}else {
语句体2;
}

执行流程:
判断比较表达式的值,看是true还是false
如果是true,就执行语句体1
如果是false,就执行语句体2

C:格式3
if(比较表达式1) {
语句体1;
}else if(比较表达式2){
语句体2;
}
...
else {
语句体n+1;
}

执行流程:
判断比较表达式1的值,看是true还是false
如果是true,就执行语句体1
如果是false,就继续判断比较表达式2的值,看是true还是false
如果是true,就执行语句体2
如果是false,就继续判断比较表达式3的值,看是true还是false
...
如果都不满足,就执行语句体n+1
(2)注意事项
A:比较表达式无论简单还是复杂,结果是boolean类型
B:if语句控制的语句体如果是一条语句,是可以省略大括号的;如果是多条,不能省略。
建议:永远不要省略。
C:一般来说,有左大括号,就没有分号,有分号,就没有左大括号。
D:else后面如果没有if,是不会出现比较表达式的。
E:三种if语句其实都是一个语句,只要有一个执行,其他的就不再执行。
(3)案例:
A:比较两个数是否相等
B:获取两个数中的最大值
C:获取三个数中的最大值(if语句的嵌套)
D:根据成绩输出对应的等级
E:根据月份,输出对应的季节
F:根据x计算对应y的值并输出
(4)三元运算符和if语句第二种格式的关系
所有的三元运算符能够实现的,if语句的第二种格式都能实现。
反之不成立。

如果if语句第二种格式控制的语句体是输出语句,就不可以。
因为三元运算符是一个运算符,必须要有一个结果返回,不能是一个输出语句。

============================================================================

1:switch语句(掌握)

(1)格式:
switch(表达式) {
case 值1:
语句体1;
break;
case 值2:
语句体2;
break;
...
default:
语句体n+1;
break;
}


格式解释说明:
switch:说明这是switch语句。
表达式:可以是byte,short,int,char
JDK5以后可以是枚举
JDK7以后可以是字符串
case:后面的值就是要和表达式进行比较的值
break:表示程序到这里中断,跳出switch语句
default:如果所有的情况都不匹配,就执行这里,相当于if语句中的else
(2)面试题
switch语句的表达式可以是byte吗?可以是long吗?可以是String吗?
可以,不可以,JDK7以后可以
(3)执行流程:
A:首先计算表达式的值
B:和每一个case进行匹配,如果有就执行对应的语句体,看到break就结束。
C:如果没有匹配,就执行default的语句体n+1。

(4)注意事项:
A:case后面只能是常量,不能是变量,而且,多个case后面的值不能出现相同的
B:default可以省略吗?
可以省略,但是不建议,因为它的作用是对不正确的情况给出提示。
特殊情况:
case就可以把值固定。
A,B,C,D
C:break可以省略吗?
可以省略,但是结果可能不是我们想要的。
会出现一个现象:case穿透。
最终我们建议不要省略

D:default一定要在最后吗?
不是,可以在任意位置。但是建议在最后。
E:switch语句的结束条件
a:遇到break就结束了
b:执行到末尾就结束了

(5)案例:
A:键盘录入一个数字(1-7),输出对应的星期几。
B:单项选择题
C:键盘录入一个字符串的问题
String s = sc.nextLine();
D:根据给定的月份,输出对应的季节
(6)if语句和switch语句各自的场景
A:if
针对boolean类型的判断
针对一个范围的判断
针对几个常量的判断
B:switch
针对几个常量的判断
 

2:循环语句(掌握)

(1)有三种:for,while,do...while
(2)for循环语句
A:格式
for(初始化语句;判断条件语句;控制条件语句){
循环体语句;
}

执行流程:
a:执行初始化语句
b:执行判断条件语句
如果这里是true,就继续
如果这里是false,循环就结束
c:执行循环体语句
d:执行控制条件语句
e:回到b
B:注意事项
a:判断条件语句无论简单还是复杂,结果是boolean类型
b:循环体语句如果是一条,可以省略大括号,但是不建议
c:有分号就没有左大括号,有左大括号就没有分号
C:案例
a:输出10次HelloWorld
b:输出1-10的数据
c:输出10-1的数据
d:求1-10的和
e:求1-100的和,求1-100的偶数和,求1-100的奇数和
f:求5的阶乘
g:在控制台打印水仙花数
h:统计水仙花个数
i:改进版的回文数
一个五位数
个位 = 万位
十位 = 千位
个位 + 十位 + 千位 + 万位 = 百位
j:统计1-1000之间同时满足如下条件的数据有多少个
x%3==2
x%5==3
x%7==2
(3)while循环
A:基本格式
while(判断条件语句) {
循环体语句;
}

扩展格式:
初始化语句;
while(判断条件语句){
循环体语句;
控制条件语句;
}

通过查看这个格式,我们就知道while循环可以和for循环等价转换。
B:while的练习
把for语句的练习用while改进
C:for和while的区别
a:使用上的区别
for语句的那个控制条件变量,在循环结束后不能在使用了。
而while的可以继续使用。
b:理解上的区别
for适合于一个范围的判断
while适合次数不明确的
举例:吃葡萄

D:案例:
a:珠穆朗玛峰问题
b:小芳存钱问题(break以后才能做)
(4)do...while循环
A:基本格式
do {
循环体语句;
}while(判断条件语句);

扩展格式:
初始化语句;
do {
循环体语句;
控制条件语句;
}while(判断条件语句);

通过查看格式,我们就可以看出其实三种循环的格式可以是统一的。
B:三种循环的区别
a:do...while循环至少执行一次循环体
b:for和while必须先判断条件是否是true,然后后才能决定是否执行循环体

(5)循环使用的注意事项(死循环)
A:一定要注意修改控制条件,否则容易出现死循环。
B:最简单的死循环格式
a:while(true){...}

b:for(;;){}

3:控制跳转语句(掌握)

(1)break:中断的意思
A:用在循环switch语句中,离开此应用场景无意义。
B:作用
a:跳出单层循环
b:跳出多层循环,需要标签语句的配合
(2)continue:继续
A:用在循环中,离开此应用场景无意义。
B:作用
a:跳出单层循环的一次,可以继续下一次
C:填空题
for(int x=1; x<=10; x++) {
if(x%3 == 0) {
//补齐代码
}
System.out.println("Java");
}
如何让控制台输出2次:Java
如何让控制台输出7次:Java
如何让控制台输出13次:Java

(3)return:返回
A:用于结束方法的,后面还会在继续讲解和使用。
B:一旦遇到return,程序就不会在继续往后执行

============================================================================

1:方法(掌握)

(1)方法:就是完成特定功能的代码块。
注意:在很多语言里面有函数的定义,而在Java中,函数被称为方法。
(2)格式:
修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...) {
方法体语句;
return 返回值;
}

修饰符:目前就用 public static。后面再详细讲解其他修饰符
返回值类型:就是功能结果的数据类型
方法名:就是起了一个名字,方便我们调用该方法。
参数类型:就是参数的数据类型
参数名:就是变量
参数分类:
实参:实际参与运算的数据
形参:方法上定义的,用于接收实际参数的变量
方法体语句:就是完成功能的代码块
return:结束方法
返回值:就是功能的结果,由return带给调用者。
(3)两个明确:
返回值类型:结果的数据类型
参数列表:参数的个数及对应的数据类型
(4)方法调用
A:有明确返回值的方法
a:单独调用,没有意义
b:输出调用,不是很好,因为我可能需要不结果进行进一步的操作。但是讲课一般我就用了。
c:赋值调用,推荐方案
B:void类型修饰的方法
a:单独调用
(5)案例:
A:求和方案
B:获取两个数中的较大值
C:比较两个数据是否相同
D:获取三个数中的最大值
E:输出m行n列的星形
F:输出nn乘法表
(6)方法的注意事项
A:方法不调用不执行
B:方法之间是平级关系,不能嵌套定义
C:方法定义的时候,参数是用,隔开的
D:方法在调用的时候,不用在传递数据类型
E:如果方法有明确的返回值类型,就必须有return语句返回。
(7)方法重载
在同一个类中,方法名相同,参数列表不同。与返回值无关。


参数列表不同:
参数的个数不同。
参数的对应的数据类型不同。

(8)方法重载案例
不同的类型的多个同名方法的比较。

2:数组(掌握)

(1)数组:存储同一种数据类型的多个元素的容器。
(2)特点:每一个元素都有编号,从0开始,最大编号是长度-1。
         编号的专业叫法:索引
(3)定义格式
A:数据类型[] 数组名;
B:数据类型 数组名[];

推荐是用A方式,B方法就忘了吧。
但是要能看懂
(4)数组的初始化
A:动态初始化
只给长度,系统给出默认值

举例:int[] arr = new int[3];
B:静态初始化
给出值,系统决定长度

举例:int[] arr = new int[]{1,2,3};
简化版:int[] arr = {1,2,3};
(5)Java的内存分配
A:栈                      存储局部变量
B:堆                      存储所有new出来的
C:方法区               (面向对象部分详细讲解)
D:本地方法区        (系统相关)
E:寄存器               (CPU使用)

注意:
a:局部变量     在方法定义中或者方法声明上定义的变量。
b:栈内存和堆内存的区别
栈:数据使用完毕,就消失。
堆:每一个new出来的东西都有地址


    每一个变量都有默认值
byte,short,int,long         0
float,double                   0.0
char                                '\u0000'
boolean                         false
引用类型                         null

    

                                    数据使用完毕后,在垃圾回收器空闲的时候回收。
(6)数组内存图
A:一个数组
B:二个数组
C:三个数组(两个栈变量指向同一个堆内存)
(7)数组的常见操作
A:遍历
方式1:
public static void printArray(int[] arr) {
for(int x=0; x<arr.length; x++) {
System.out.println(arr[x]);
}
}

方式2:
public static void printArray(int[] arr) {
System.out.print("[");
for(int x=0; x<arr.length; x++) {
if(x == arr.length-1) {
System.out.println(arr[x]+"]");
}else {
System.out.println(arr[x]+", ");
}
}
}
B:最值
最大值:
public static int getMax(int[] arr) {
int max = arr[0];

for(int x=1; x<arr.length; x++) {
if(arr[x] > max) {
max = arr[x];
}
}

return max;
}

最小值:
public static int getMin(int[] arr) {
int min = arr[0];

for(int x=1; x<arr.length; x++) {
if(arr[x] < min) {
min = arr[x];
}
}

return min;
}
C:逆序
方式1:
public static void reverse(int[] arr) {
for(int x=0; x<arr.length/2; x++) {
int temp = arr[x];
arr[x] = arr[arr.length-1-x];
arr[arr.length-1-x] = temp;
}
}

方式2:
public static void reverse(int[] arr) {
for(int start=0,end=arr.length-1; start<=end; start++,end--) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
}
D:查表
public static String getString(String[] strArray,int index) {
return strArray[index];
}
E:基本查找
方式1:
public static int getIndex(int[] arr,int value) {
for(int x=0; x<arr.length; x++) {
if(arr[x] == value) {
return x;
}
}

return -1;
}

方式2:
public static int getIndex(int[] arr,int value) {
int index = -1;

for(int x=0; x<arr.length; x++) {
if(arr[x] == value) {
index = x;
break;
}
}

return index;
}

============================================================================

1:二维数组(理解)

(1)元素是一维数组的数组。
(2)格式:
A:数据类型[][] 数组名 = new 数据类型[m][n];
B:数据类型[][] 数组名 = new 数据类型[m][];
C:数据类型[][] 数组名 = new 数据类型[][]{{...},{...},{...}};
D:数据类型[][] 数组名 = {{...},{...},{...}};
(3)案例(掌握):
A:二维数组的遍历
B:二维数组的求和
C:杨辉三角形
 

2:两个思考题(理解)

(1)Java中的参数传递问题
Java中只有值传递

基本类型:形式参数的改变不影响实际参数
引用类型:形式参数的改变直接影响实际参数
(2)数据加密问题
综合的小案例。

3:面向对象(掌握)

(1)面向对象
面向对象是基于面向过程的编程思想
(2)面向对象的思想特点
A:是一种更符合我们思考习惯的思想
B:把复杂的事情简单化
C:让我们从执行者变成了指挥者

举例:
买电脑
洗衣服
做饭
...
万事万物皆对象
(3)把大象装进冰箱(理解)
A:面向过程实现
B:面向对象实现

注意:如何让我们的操作更符合面向对象思想呢?
A:有哪些类
B:每个类有哪些成员
C:类与类的关系
(4)类与对象
A:现实世界的事物
属性 事物的基本描述
行为 事物的功能
B:Java语言中最基本的单位是类。所以,我们要用类来体现事物
C:类
成员变量 事物属性
成员方法 事物行为

D:    类:是一组相关的属性和行为的集合。是一个抽象的概念。
       对象:是该类事物的具体存在,是一个具体的实例。(对象)

  
  举例:
学生:类
班长:对象
(5)类的定义及使用
A:类的定义
成员变量 定义格式和以前一样,就是位置不同,在类中,方法外。
成员方法 定义格式和以前一样,就是去掉了static。
B:使用类的内容
a:创建对象? 格式
类名 对象名 =  new 类名();
b:如何使用成员变量和成员方法呢
对象名.成员变量
对象名.成员方法()
(6)案例:
A:学生类的定义和使用
B:手机类的定义和使用
(7)内存图
A:一个对象的内存图
B:二个对象的内存图
C:三个对象的内存图
(8)Java程序的开发,设计和特征
A:开发:就是不断的创建对象,通过对象调用功能
B:设计:就是管理和维护对象间的关系
C:特征
a:封装
b:继承
c:多态

============================================================================

1:成员变量和局部变量的区别(理解)

(1)在类中的位置不同
成员变量:类中方法外
局部变量:方法定义中或者方法声明上
(2)在内存中的位置不同
成员变量:在堆中
局部变量:在栈中
(3)生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
(4)初始化值不同
成员变量:有默认值
局部变量:没有默认值,必须定义,赋值,然后才能使用

2:类作为形式参数的问题?(理解)

(1)如果你看到一个方法需要的参数是一个类名,就应该知道这里实际需要的是一个具体的对象
 

3:匿名对象(理解)

(1)没有名字的对象
(2)应用场景
A:调用方法,仅仅只调用一次的时候
b:可以作为实际参数传递

4:封装(理解)

(1)隐藏实现细节,提供公共的访问方式
(2)好处:
A:隐藏实现细节,提供公共的访问方式
B:提高代码的复用性
C:提高代码的安全性
(3)设计原则
把不想让外界知道的实现细节给隐藏起来,提供公共的访问方式
(4)private是封装的一种体现。
封装:类,方法,private修饰成员变量
 

5:private关键字(掌握)

(1)私有的意义,可以修饰成员变量和成员方法
(2)特点:
被private修饰的后的成员只能在本类中被访问
(3)private的应用:
以后再写一个类的时候:
把所有的成员变量给private了
提供对应的getXxx()/setXxx()方法
 

6:this关键字(掌握)

(1)代表当前类的引用对象
记住:哪个对象调用方法,该方法内部的this就代表那个对象
(2)this的应用场景:
A:解决了局部变量隐藏成员变量的问题
B:其实this还有其他的应用,明天讲解。
 

7:构造方法(掌握)

(1)作用:用于对对象的数据进行初始化
(2)格式:
A:方法名和类名相同
B:没有返回值类型,连void都不能有
C:没有返回值


思考题:构造方法中可不可以有return语句呢?
可以。而是我们写成这个样子就OK了:return;
其实,在任何的void类型的方法的最后你都可以写上:return;
(3)构造方法的注意事项
A:如果我们没写构造方法系统将提供一个默认的无参构造方法
B:如果我们给出了构造方法,系统将不再提供默认构造方法
如果这个时候,我们要使用无参构造方法,就必须自己给出。
推荐:永远手动自己给出无参构造方法。
(4)给成员变量赋值的方式
A:setXxx()
B:带参构造方法
(5)标准案例
class Student {
private String name;
private int age;

public Student(){}

public Student(String name,int age) {
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;
}
}

测试:
class StudentDemo {
public static void main(String[] args) {
//方式1
Student s1 = new Student();
s1.setName("林青霞");
s1.setAge(27);
System.out.println(s1.getName()+"---"+s1.getAge());

//方式2
Student s2 = new Student("刘意",30);
System.out.println(s2.getName()+"---"+s2.getAge());
}
}

 

8:代码:Student s = new Student();做了哪些事情?(理解)

(1)把Student.class文件加载到内存
(2)在栈内存为s开辟空间
(3)在堆内存为学生对象申请空间
(4)给学生的成员变量进行默认初始化。null,0
(5)给学生的成员变量进行显示初始化。林青霞,27
(6)通过构造方法给成员变量进行初始化。刘意,30
(7)对象构造完毕,把地址赋值给s变量

9:面向对象的练习题(掌握)

(1)标准的手机类的定义和测试
(2)Demo类有求和方法,Test类进行测试。
什么时候定义成员变量?
当该变量是用来描述一个类的时候。
(3)长方形案例
(4)员工案例
(5)MyMath案例(自己提供加减乘除并测试)

10:static关键字(理解)

(1)静态的意思。可以修饰成员变量和成员方法。
(2)静态的特点:
A:随着类的加载而加载
B:优先于对象存在
C:被类的所有对象共享
这其实也是我们判断该不该使用静态的依据。
举例:饮水机和水杯的问题思考

D:可以通过类名调用
既可以通过对象名调用,也可以通过类名调用,建议通过类名调用。
(3)静态的内存图
静态的内容在方法区的静态区
(4)静态的注意事项;
A:在静态方法中没有this对象
B:静态只能访问静态(代码测试过)
(5)静态变量和成员变量的区别
A:所属不同
静态变量:属于类,类变量
成员变量:属于对象,对象变量,实例变量
B:内存位置不同
静态变量:方法区的静态区
成员变量:堆内存
C:生命周期不同
静态变量:静态变量是随着类的加载而加载,随着类的消失而消失
成员变量:成员变量是随着对象的创建而存在,随着对象的消失而消失
D:调用不同
静态变量:可以通过对象名调用,也可以通过类名调用
成员变量:只能通过对象名调用

(6)main方法是静态的
public:权限最大
static:不用创建对象调用
void:返回值给jvm没有意义
main:就是一个常见的名称。
String[] args:可以接收数据,提供程序的灵活性
格式:java MainDemo hello world java
  java MainDemo 10 20 30

============================================================================

1:如何制作帮助文档(了解)

(1)写一个类
(2)加入文档注释
(3)通过javadoc工具生成即可
javadoc -d 目录 -author -version ArrayTool.java
 

2:通过JDK提供的API学习了Math类(掌握)

(1)API(Application Programming Interface)
应用程序编程接口(帮助文档)
(2)如何使用呢?
请参照
day08\code\02_如何使用JDK提供的帮助文档\如何使用帮助文档.txt
(3)Math类
A:是针对数学进行操作的类
B:没有构造方法,因为它的成员都是静态的
C:产生随机数
public static double random(): [0.0,1.0)
D:如何产生一个1-100之间的随机数
int number = (int)(Math.random()*100)+1;
E:猜数字小游戏
 

3:代码块(理解)

(1)用{}括起来的代码。
(2)分类:
A:局部代码块
用于限定变量的生命周期,及早释放,提高内存利用率。
B:构造代码块
把多个构造方法中相同的代码可以放到这里,每个构造方法执行前,首先执行构造代码块。
C:静态代码块
对类的数据进行初始化,仅仅只执行一次。
(3)静态代码块,构造代码块,构造方法的顺序问题?
静态代码块 > 构造代码块 > 构造方法

4:继承(掌握)

(1)把多个类中相同的成员给提取出来定义到一个独立的类中。然后让这多个类和该独立的类产生一个关系,
   这多个类就具备了这些内容。这个关系叫继承。
(2)Java中如何表示继承呢?格式是什么呢?
A:用关键字extends表示
B:格式:
class 子类名 extends 父类名 {}
(3)继承的好处:
A:提高了代码的复用性
B:提高了代码的维护性
C:让类与类产生了一个关系,是多态的前提
(4)继承的弊端
A:让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。
原则:低耦合,高内聚。
耦合:类与类的关系
内聚:自己完成某件事情的能力
B:打破了封装性
(5)Java中继承的特点
A:Java中类只支持单继承
B:Java中可以多层(重)继承(继承体系)
(6)继承的注意事项:
A:子类不能继承父类的私有成员
B:子类不能继承父类的构造方法,但是可以通过super去访问
C:不要为了部分功能而去继承

(7)什么时候使用继承呢?
A:继承体现的是:is a的关系。
B:采用假设法
(8)Java继承中的成员关系
A成员变量
a:子类的成员变量名称和父类中的成员变量名称不一样,这个太简单
b:子类的成员变量名称和父类中的成员变量名称一样,这个怎么访问呢?
子类的方法访问变量的查找顺序:
在子类方法的局部范围找,有就使用。
在子类的成员范围找,有就使用。
在父类的成员范围找,有就使用。
找不到,就报错。

B:构造方法
a:子类的构造方法默认会去访问父类的无参构造方法
是为了子类访问父类数据的初始化
b:父类中如果没有无参构造方法,怎么办?
子类通过super去明确调用带参构造
子类通过this调用本身的其他构造,但是一定会有一个去访问了父类的构造
让父类提供无参构造

C:成员方法
a:子类的成员方法和父类中的成员方法名称不一样,这个太简单
b:子类的成员方法和父类中的成员方法名称一样,这个怎么访问呢?
通过子类对象访问一个方法的查找顺序:
在子类中找,有就使用
在父类中找,有就使用
找不到,就报错

(9)两个面试题:
A:Override和Overload的区别?Overload是否可以改变返回值类型?
B:this和super的区别和各自的作用?
:
(10)数据初始化的面试题
A:一个类的初始化过程
B:子父类的构造执行过程
C:分层初始化
(11)案例:
A:学生和老师案例
继承前
继承后
B:猫狗案例的分析和实现

============================================================================

1:final关键字(掌握)

(1)是最终的意思,可以修饰类,方法,变量
(2)特点:
A:它修饰的类,不能被继承
B:它修饰的方法,不能被重写
C:它修饰的变量,是一个常量
(3)面试相关:
A:局部变量
a:基本类型 不能发生改变
b:引用类型 地址值不能发生改变,但是对象的内容是可以改变
B:初始化时机
a:只能初始化一次。
b:常见的给值
定义的时候。(推荐)
构造方法中。

2:多态(掌握)

(1)同一个对象在不同时刻体现出来的不同状态
(2)多态的前提:
A:有继承或者实现关系。
B:有方法重写
C:有父类或者父接口引用指向子类对象。

多态的分类:
a:具体类多态
class Fu {}
class Zi extends Fu {}

Fu f = new Zi();
b:抽象类多态
abstract class Fu {}
class Zi extends Fu {}

Fu f = new Zi();
c:接口多态
interface Fu {}
class Zi implements Fu {}

Fu f = new Zi();
(3)多态中的成员访问特点
A:成员变量
编译看左边,运行看左边
B:构造方法
子类的构造都会默认访问父类构造
C:成员方法
编译看左边,运行看右边
D:静态方法
编译看左边,运行看左边


为什么?
因为成员方法有重写。
(4)多态的好处:
A:提高代码的维护性(继承体现)
B:提高代码的扩展性(多态体现)
(5)多态的弊端:
父不能使用子的特有功能。


现象:
子可以当作父使用,父不能当作子使用。
(6)多态中的转型
A:向上转型
从子到父
B:向下转型
从父到子

(7)孔子装爹的案例帮助大家理解多态
(8)多态的练习
A:猫狗案例
B:老师和学生案例
 

3:抽象类(掌握)

(1)把多个共性的东西提取到一个类中,这是继承的做法。
   但是呢,这多个共性的东西,在有些时候,方法声明一样,但是方法体。
   也就是说,方法声明一样,但是每个具体的对象在具体实现的时候内容不一样。
   所以,我们在定义这些共性的方法的时候,就不能给出具体的方法体。
   而一个没有具体的方法体的方法是抽象的方法
   在一个类中如果有抽象方法,该类必须定义为抽象类
(2)抽象类的特点
A:抽象类和抽象方法必须用关键字abstract修饰
B:抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
C:抽象类不能实例化
D:抽象类的子类
a:是一个抽象类
b:是一个具体类。这个类必须重写抽象类中的所有抽象方法
(3)抽象类的成员特点:
A:成员变量
有变量,有常量
B:构造方法
有构造方法
C:成员方法
有抽象,有非抽象
(4)抽象类的练习
A:猫狗案例练习
B:老师案例练习
C:学生案例练习
D:员工案例练习
(5)抽象类的几个小问题
A:抽象类有构造方法,不能实例化,那么构造方法有什么用?
用于子类访问父类数据的初始化

B:一个类如果没有抽象方法,却定义为了抽象类,有什么用?
为了不让创建对象

C:abstract不能和哪些关键字共存
a:final        冲突
b:private   冲突
c:static      无意义

 

4:接口(掌握)

(1)回顾猫狗案例,它们仅仅提供一些基本功能。
   比如:猫钻火圈,狗跳高等功能,不是动物本身就具备的,
   是在后面的培养中训练出来的,这种额外的功能,java提供了接口表示。
(2)接口的特点:
A:接口用关键字interface修饰
interface 接口名 {}
B:类实现接口用implements修饰
class 类名 implements 接口名 {}
C:接口不能实例化
D:接口的实现类
a:是一个抽象类。
b:是一个具体类,这个类必须重写接口中的所有抽象方法。

(3)接口的成员特点:
A:成员变量
只能是常量
默认修饰符:public static final

B:构造方法
没有构造方法
C:成员方法
只能是抽象的
默认修饰符:public abstract
(4)类与类,类与接口,接口与接口
A:类与类
继承关系,只能单继承,可以多层继承
B:类与接口
实现关系,可以单实现,也可以多实现。
类还可以在继承一个类的同时,实现多个接口
C:接口与接口
继承关系,可以单继承,也可以多继承

(5)抽象类和接口的区别

                A:成员区别
                抽象类:
            成员变量:可以变量,也可以常量
            构造方法:有
            成员方法:可以抽象,也可以非抽象
                接口:
            成员变量:只可以常量
            成员方法:只可以抽象

                B:关系区别
                 类与类
               继承,单继承
                类与接口
                实现,单实现,多实现
                接口与接口
                继承,单继承,多继承

                C:设计理念区别
                抽象类     被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
                接口         被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。

        (6)练习:
A:猫狗案例,加入跳高功能
B:老师和学生案例,加入抽烟功能0

 

============================================================================

1:形式参数和返回值的问题(理解)

(1)形式参数:
类名:需要该类的对象
抽象类名:需要该类的子类对象
接口名:需要该接口的实现类对象
(2)返回值类型:
类名:返回的是该类的对象
抽象类名:返回的是该类的子类对象
接口名:返回的是该接口的实现类的对象
(3)链式编程
对象.方法1().方法2().......方法n();

这种用法:其实在方法1()调用完毕后,应该一个对象;
      方法2()调用完毕后,应该返回一个对象。
  方法n()调用完毕后,可能是对象,也可以不是对象。
 

2:包(理解)

(1)其实就是文件夹
(2)作用:
A:区分同名的类
B:对类进行分类管理
a:按照功能
b:按照模块
(3)包的定义(掌握)
package 包名;

多级包用.分开。
(4)注意事项:(掌握)
A:package语句必须在文件中的第一条有效语句
B:在一个java文件中,只能有一个package
C:如果没有package,默认就是无包名
(5)带包的编译和运行
A:手动式
B:自动式(掌握)
javac -d . HelloWorld.java

3:导包(掌握)

(1)我们多次使用一个带包的类,非常的麻烦,这个时候,Java就提供了一个关键字import。
(2)格式:
import 包名...类名;
另一种:
import 包名...*;(不建议)
(3)package,import,class的顺序
package > import > class

4:权限修饰符(掌握)

(1)权限修饰符
本类 同一个包下 不同包下的子类 不同包下的无关类
private Y
默认         Y Y
protected Y Y Y
public Y Y Y Y

(2)这四种权限修饰符在任意时刻只能出现一种
public class Demo {}
 

5:常见的修饰符(理解)

(1)分类:
权限修饰符:private,默认,protected,public
状态修饰符:static,final
抽象修饰符:abstract
(2)常见的类及其组成的修饰

默认,public,final,abstract

常用的:public

成员变量
private,默认,protected,public,static,final

常用的:private

构造方法
private,默认,protected,public

常用的:public

成员方法
private,默认,protected,public,static,final,abstract

常用的:public
(3)另外比较常见的:
public static final int X = 10;
public static void show() {}
public final void show() {}
public abstract void show();
 

6:内部类(理解)

(1)把类定义在另一个类的内部,该类就被称为内部类。
举例:把类B定义在类A中,类B就被称为内部类。
(2)内部类的访问规则
A:可以直接访问外部类的成员,包括私有
B:外部类要想访问内部类成员,必须创建对象
(3)内部类的分类
A:成员内部类
B:局部内部类
(4)成员内部类
A:private 为了数据的安全性
B:static 为了访问的方便性

成员内部类不是静态的:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
成员内部类是静态的:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
(5)成员内部类的面试题(填空)
30,20,10

class Outer {
public int num = 10;

class Inner {
public int num = 20;

public viod show() {
int num  = 30;

System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);
}
}
}
(6)局部内部类
A:局部内部类访问局部变量必须加final修饰
B:为什么呢?
因为局部变量使用完毕就消失,而堆内存的数据并不会立即消失。
所以,堆内存还是用该变量,而改变量已经没有了。
为了让该值还存在,就加final修饰。
通过反编译工具我们看到了,加入final后,堆内存直接存储的是值,而不是变量名。
(7)匿名内部类(掌握)
A:是局部内部类的简化形式
B:前提
存在一个类或者接口
C:格式:
new 类名或者接口名() {
重写方法;
}

D:本质:
其实是继承该类或者实现接口的子类匿名对象
(8)匿名内部类在开发中的使用
我们在开发的时候,会看到抽象类,或者接口作为参数。
而这个时候,我们知道实际需要的是一个子类对象。
如果该方法仅仅调用一次,我们就可以使用匿名内部类的格式简化

interface Person {
public abstract void study();
}

class PersonDemo {
public void method(Person p) {
p.study();
}
}

class PersonTest {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
pd.method(new Person() {
public void study() {
System.out.println("好好学习,天天向上");
}
});
}
}

(9)匿名内部类的面试题(补齐代码)
interface Inter {
void show();
}

class Outer {
//补齐代码
public static Inter method() {
return new Inter() {
public void show() {
System.out.println("HelloWorld");
}
};
}
}

class OuterDemo {
public static void main(String[] args) {
Outer.method().show(); //"HelloWorld"
}
}

============================================================================

1:Eclipse的概述使用(掌握)

请参照ppt和课堂练习.txt

2:API的概述(了解)

(1)应用程序编程接口。
(2)就是JDK提供给我们的一些提高编程效率的java类。
 

3:Object类(掌握)

(1)Object是类层次结构的根类所有的类都直接或者间接的继承自Object类
(2)Object类的构造方法有一个,并且是无参构造
这其实就是理解当时我们说过,子类构造方法默认访问父类的构造是无参构造
(3)要掌握的方法:
A:toString()
返回对象的字符串表示,默认是由类的全路径+'@'+哈希值的十六进制表示。
这个表示其实是没有意义的,一般子类都会重写该方法。
如何重写呢?过程我也讲解过了,基本上就是要求信息简单明了。
但是最终还是自动生成。
B:equals()
比较两个对象是否相同。默认情况下,比较的是地址值是否相同。
比较地址值是没有意义的,所以,一般子类也会重写该方法。
重写过程,我也详细的讲解和分析了。
但是最终还是自动生成。
(4)要了解的方法:
A:hashCode()      返回对象的哈希值。不是实际地址值,可以理解为地址值。
B:getClass()         返回对象的字节码文件对象,反射中我们会详细讲解
C:finalize()           用于垃圾回收,在不确定的时间
D:clone()             可以实现对象的克隆,包括成员变量的数据复制,但是它和两个引用指向同一个对象是有区别的
(5)两个注意问题;
A:直接输出一个对象名称,其实默认调用了该对象的toString()方法。System.out.println()
B:面试题 
==和equals()的区别?
A:==
基本类型:比较的是值是否相同
引用类型:比较的是地址值是否相同
B:equals()
只能比较引用类型。默认情况下,比较的是地址值是否相同。
但是,我们可以根据自己的需要重写该方法。

============================================================================

1:Scanner的使用(了解)

(1)在JDK5以后出现的用于键盘录入数据的类。
(2)构造方法:
A:讲解了System.in这个东西。
它其实是标准的输入流,对应于键盘录入
B:构造方法
InputStream is = System.in;

Scanner(InputStream is)
C:常用的格式
Scanner sc = new Scanner(System.in);
(3)基本方法格式:
A:hasNextXxx() 判断是否是某种类型的
B:nextXxx() 返回某种类型的元素
(4)要掌握的两个方法
A:public int nextInt()
B:public String nextLine()
(5)需要注意的小问题
A:同一个Scanner对象,先获取数值,再获取字符串会出现一个小问题。
B:解决方案:
a:重新定义一个Scanner对象
b:把所有的数据都用字符串获取,然后再进行相应的转换

2:String类的概述和使用(掌握)

(1)多个字符组成的一串数据。
其实它可以和字符数组进行相互转换。

                字符串字面值"abc"也可以看成是一个字符串对象。

(2)构造方法:

                public String()                                                                空构造
  public String(byte[] bytes)                                             把字节数组转成字符串
  public String(byte[] bytes,int index,int length)              把字节数组的一部分转成字符串,第二个参数index表示从

                                                                                                       当前索引开始


  public String(char[] value)                                              把字符数组转成字符串
  public String(char[] value,int index,int count)                把字符数组的一部分转成字符串
  public String(String original)                                          把字符串常量值转成字符串

                

                下面的这一个虽然不是构造方法,但是结果也是一个字符串对象
        String s = "hello";

 

public static void main(String[] args) {
    /**
     * public String():空构造
     */
    String s1 = new String();
    System.out.println("s1:" + s1);//s1:
    System.out.println("s1.length():" + s1.length());//s1.length():0
    System.out.println("--------------------------");

    /**
     * public String(byte[] bytes):把字节数组转成字符串
     */
    byte[] bys = { 97, 98, 99, 100, 101 };
    String s2 = new String(bys);
    System.out.println("s2:" + s2);//abcde
    System.out.println("s2.length():" + s2.length());//5
    System.out.println("--------------------------");


    /**
     * public String(byte[] bytes,int index,int length):把字节数组的一部分转成字符串
     * 第二个参数index表示从当前索引开始
     */
    // 我想得到字符串"bcd"
    String s3 = new String(bys, 1, 3);
    System.out.println("s3:" + s3);//bcd
    System.out.println("s3.length():" + s3.length());//3
    System.out.println("--------------------------");

    /**
     * public String(char[] value):把字符数组转成字符串
     */
    char[] chs = { 'a', 'b', 'c', 'd', 'e', '爱', '林', '亲' };
    String s4 = new String(chs);
    System.out.println("s4:" + s4);//abcde爱林亲
    System.out.println("s4.length():" + s4.length());//8
    System.out.println("--------------------------");

    /**
     * public String(char[] value,int index,int count):把字符数组的一部分转成字符串
     * 第二个参数index表示从当前索引开始
     */
    String s5 = new String(chs, 2, 4);
    System.out.println("s5:" + s5);//cde爱
    System.out.println("s5.length():" + s5.length());//4
    System.out.println("--------------------------");

    /**
     * public String(String original):把字符串常量值转成字符串
     */
    String s6 = new String("abcde");
    System.out.println("s6:" + s6);//adcde
    System.out.println("s6.length():" + s6.length());//5
    System.out.println("--------------------------");

    /**
     * 字符串字面值"abc"也可以看成是一个字符串对象。
     */
    String s7 = "abcde";
    System.out.println("s7:"+s7);//abcde
    System.out.println("s7.length():"+s7.length());//5
}


(3)字符串的特点
A:字符串一旦被赋值,就不能改变
注意:这里指的是字符串的内容不能改变,而不是引用不能改变。     

     

    String s = "hello";
    s += "world";
    s += "haha";
    System.out.println("s:" + s); // helloworldhaha

B:字面值作为字符串对象和通过构造方法创建对象的不同
String s = new String("hello");  和String s = "hello" 的区别?

                  答:有。前者会创建2个对象,后者创建1个对象。

                         ==:比较引用类型比较的是地址值是否相同
                         equals:比较引用类型默认也是比较地址值是否相同,而String类重写了equals()方法,比较的是内容是否相同。

 

 

    String s1 = new String("hello");
    String s2 = "hello";

    System.out.println(s1 == s2);// false
    System.out.println(s1.equals(s2));// true

(4)字符串的面试题(看程序写结果)
A:==和equals()

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);// false
System.out.println(s1.equals(s2));// true

String s3 = new String("hello"); 
String s4 = "hello";
// 前者会创建2个对象,后者创建1个对象
System.out.println(s3 == s4);// false
System.out.println(s3.equals(s4));// true

String s5 = "hello";
String s6 = "hello";
System.out.println(s5 == s6);// true 引用的同一个方法区里的常量池对象
System.out.println(s5.equals(s6));// true

B:字符串的拼接

                     字符串如果是 变量 相加,先开空间,再拼接。
                     字符串如果是 常量 相加,是先加,然后在常量池找,如果有就直接返回,否则,就创建。

String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";

System.out.println(s3 == s1 + s2);// false
System.out.println(s3.equals((s1 + s2)));// true

System.out.println(s3 == "hello" + "world");// false 这个我们错了,应该是true
System.out.println(s3.equals("hello" + "world"));// true

// 通过反编译看源码,我们知道这里已经做好了处理。
// System.out.println(s3 == "helloworld");
// System.out.println(s3.equals("helloworld"));


(5)字符串的功能(自己补齐方法中文意思)
A:判断功能
 * boolean equals(Object obj):                 比较字符串的内容是否相同,区分大小写
 * boolean equalsIgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
 * boolean contains(String str):                判断大字符串中是否包含小字符串
 * boolean startsWith(String str):             判断字符串是否以某个指定的字符串开头
 * boolean endsWith(String str):              判断字符串是否以某个指定的字符串结尾
 * boolean isEmpty():                                判断字符串是否为空。

字符串内容为空和字符串对象为空。
String s = "";
String s = null;

对象都不存在,所以不能调用方法,空指针异常

B:获取功能
int length()                                             返回此字符串的长度
char charAt(int index)                            获取指定索引位置的字符
int indexOf(int ch)                                  返回指定字符在此字符串中第一次出现处的索引。

                                                                                            为什么这里是int类型,而不是char类型?
                                                                                            原因是:'a'和97其实都可以代表'a'


int indexOf(String str)                             返回指定字符串在此字符串中第一次出现处的索引
int indexOf(int ch,int fromIndex)            返回指定 字符 在此字符串中从指定位置后第一次出现处的索引。
int indexOf(String str,int fromIndex)      返回指定 字符串 在此字符串中从指定位置后第一次出现处的索引。
String substring(int start)                        从指定位置开始截取字符串,默认到末尾。
String substring(int start,int end)            从指定位置开始到指定位置结束截取字符串。

 

                 
C:转换功能
byte[] getBytes()                             把字符串转换为字节数组。
char[] toCharArray()                        把字符串转换为字符数组。
static String valueOf(char[] chs)     把字符数组转成字符串。
static String valueOf(int i)               把int类型的数据转成字符串。

                                                      注意:String类的valueOf方法可以把任意类型的数据转成字符串。


String toLowerCase()                      把字符串转成小写。
String toUpperCase()                      把字符串转成大写。
String concat(String str)                 把字符串拼接。

 

D:其他功能
a:替换功能 
String replace(char old,char new)
String replace(String old,String new)
b:去空格功能 去掉两端空格,中间的空格不能取
String trim()
c:按字典比较功能
int compareTo(String str)
int compareToIgnoreCase(String str) 
(6)字符串的案例
A:模拟用户登录

// 定义用户名和密码。已存在的。
String username = "admin";
String password = "admin";

// 给三次机会,用循环改进,最好用for循环。
for (int x = 0; x < 3; x++) {
    // x=0,1,2
    // 键盘录入用户名和密码。
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入用户名:");
    String name = sc.nextLine();
    System.out.println("请输入密码:");
    String pwd = sc.nextLine();

    // 比较用户名和密码。
    if (name.equals(username) && pwd.equals(password)) {
        // 如果都相同,则登录成功
        System.out.println("登录成功,开始玩游戏");
        //猜数字游戏
        GuessNumberGame.start();
        break;
    } else {
        // 如果有一个不同,则登录失败
        // 2,1,0
        // 如果是第0次,应该换一种提示
        if ((2 - x) == 0) {
            System.out.println("帐号被锁定,请与班长联系");
        } else {
            System.out.println("登录失败,你还有" + (2 - x) + "次机会");
        }
    }
}

B:字符串遍历

// 定义字符串
String s = "helloworld";

// 如果长度特别长,我不可能去数,所以我们要用长度功能
for (int x = 0; x < s.length(); x++) {
    // char ch = s.charAt(x);
    // System.out.println(ch);
    // 仅仅是输出,我就直接输出了
    System.out.println(s.charAt(x));
}

C:统计字符串中大写,小写及数字字符的个数

//定义一个字符串
String s = "Hello123World";

//定义三个统计变量
int bigCount = 0;
int smallCount = 0;
int numberCount = 0;

//遍历字符串,得到每一个字符。
for (int x=0; x < s.length(); x++) {
    char ch = s.charAt(x);

    // TODO 判断该字符到底是属于那种类型的***********
    if (ch>='a' && ch<='z') {
        smallCount ++;
    } else if (ch >= 'A' && ch <= 'Z'){
        bigCount ++;
    } else if (ch >= '0' && ch <= '9'){
        numberCount ++;
    }
}

//输出结果。
System.out.println("大写字母"+bigCount+"个");
System.out.println("小写字母"+smallCount+"个");
System.out.println("数字"+numberCount+"个");

D:把字符串的首字母转成大写,其他小写

// 定义一个字符串
String s = "helloWORLD";

// 链式编程
String result = s.substring(0, 1).toUpperCase()
        .concat(s.substring(1).toLowerCase());

System.out.println(result);

E:把int数组拼接成一个指定格式的字符串

public static void main(String[] args) {
       // 前提是数组已经存在
       int[] arr = {1, 2, 3};

       // 写一个功能,实现结果
       String result = arrayToString(arr);
       System.out.println("最终结果是:" + result);
   }

/*
 * 两个明确: 返回值类型:String 参数列表:int[] arr
 */
   public static String arrayToString(int[] arr) {
       // 定义一个字符串
       String s = "";

       // 先把字符串拼接一个"["
       s += "[";

       // 遍历int数组,得到每一个元素
       for (int x = 0; x < arr.length; x++) {
           // 先判断该元素是否为最后一个
           if (x == arr.length - 1) {
               // 就直接拼接元素和"]"
               s += arr[x];
               s += "]";
           } else {
               // 就拼接元素和逗号以及空格
               s += arr[x];
               s += ", ";
           }
       }

       return s;
   }

F:字符串反转

// 定义一个新字符串
String result = "";

// 把字符串转成字符数组
char[] chs = s.toCharArray();

// 倒着遍历字符串,得到每一个字符
for (int x = chs.length - 1; x >= 0; x--) {
    // 用新字符串把每一个字符拼接起来
    result += chs[x];
}

return result;

G:统计大串中小串出现的次数

public static void main(String[] args) {
      // 定义大串
      String maxString = "woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun";
      // 定义小串
      String minString = "java";

      // 写功能实现
      int count = getCount(maxString, minString);
      System.out.println("Java在大串中出现了:" + count + "次");
  }

  /*
   * 两个明确: 返回值类型:int 参数列表:两个字符串
   */
  public static int getCount(String maxString, String minString) {
      // 定义一个统计变量,初始化值是0
      int count = 0;
      int index;
      //先查,赋值,判断
      while((index = maxString.indexOf(minString)) != -1){
          count++;
          maxString = maxString.substring(index + minString.length());
      }

      return count;
  }

图解:

============================================================================

1:StringBuffer(掌握)

(1)用字符串做拼接,比较耗时并且也耗内存,而这种拼接操作又是比较常见的,为了解决这个问题,Java就提供了
   一个字符串缓冲区类。StringBuffer供我们使用。

 

线程安全(多线程讲解)
 * 安全 -- 同步 -- 数据是安全的
 * 不安全 -- 不同步 -- 效率高一些

 * 安全和效率问题是永远困扰我们的问题。
 * 安全:医院的网站,银行网站
 * 效率:新闻网站,论坛之类的
 *
 * StringBuffer:
 * 线程安全的可变字符串。
 *
 * StringBuffer和String的区别?
 * 前者长度和内容可变,后者不可变。
 * 如果使用前者做字符串的拼接,不会浪费太多的资源。

 *
 * StringBuffer的构造方法:
 * public StringBuffer():无参构造方法
 * public StringBuffer(int capacity):指定容量的字符串缓冲区对象  默认16
 * public StringBuffer(String str):指定字符串内容的字符串缓冲区对象
 *
 * StringBuffer的方法:
 * public int capacity():返回当前容量。默认值16+当前长度 理论值
 * public int length():返回长度(字符数)。 实际值

 

/**
 * public StringBuffer()
 * 无参构造方法
 */
StringBuffer sb = new StringBuffer();
System.out.println("sb:" + sb);
System.out.println("sb.capacity():" + sb.capacity()); // 16
System.out.println("sb.length():" + sb.length()); // 0
System.out.println("--------------------------");

/**
 * public StringBuffer(int capacity)
 * 指定容量的字符串缓冲区对象
 */
StringBuffer sb2 = new StringBuffer(50);
System.out.println("sb2:" + sb2);
System.out.println("sb2.capacity():" + sb2.capacity()); //50
System.out.println("sb2.length():" + sb2.length()); // 0
System.out.println("--------------------------");

/**
 * public StringBuffer(String str)
 * 指定字符串内容的字符串缓冲区对象
 */
StringBuffer sb3 = new StringBuffer("hello");
System.out.println("sb3:" + sb3);
System.out.println("sb3.capacity():" + sb3.capacity()); // 16+5 = 21
System.out.println("sb3.length():" + sb3.length()); //5

(3)StringBuffer的常见功能(自己补齐方法的声明和方法的解释)
A:添加功能

 * public StringBuffer append(String str):可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
 * public StringBuffer insert(int offset,String str):在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身

 

// 创建字符串缓冲区对象
StringBuffer sb = new StringBuffer();

/**
 * public StringBuffer append(String str)
 * 可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
 */
sb.append("hello").append(true).append(12).append(34.56);
System.out.println("sb:" + sb);

/**
 * public StringBuffer insert(int offset,String str)
 * 在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
 */
sb.insert(5, "world");
System.out.println("sb:" + sb);

B:删除功能

 * public StringBuffer deleteCharAt(int index):删除指定位置的字符,并返回本身
 * public StringBuffer delete(int start,int end):删除从指定位置开始指定位置结束的内容,并返回本身

 

// 创建对象
StringBuffer sb = new StringBuffer();

// 添加功能
sb.append("hello").append("world").append("java");
System.out.println("sb:" + sb);

/**
 * public StringBuffer deleteCharAt(int index)
 * 删除指定位置的字符,并返回本身
 */
// 需求:我要删除e这个字符,肿么办?
sb.deleteCharAt(1);
System.out.println("deleteCharAt:" + sb);
// 需求:我要删除第一个l这个字符,肿么办?
// sb.deleteCharAt(1);

/**
 * public StringBuffer delete(int start,int end):删除从指定位置开始指定位置结束的内容,并返回本身
 */
// 需求:我要删除world这个字符串,肿么办?
sb.delete(4, 9);
System.out.println(sb);

// 需求:我要删除所有的数据
sb.delete(0, sb.length());
System.out.println("sb:" + sb);

C:替换功能

 * public StringBuffer replace(int start,int end,String str):从start开始到end用str替换

// 创建字符串缓冲区对象
StringBuffer sb = new StringBuffer();

// 添加数据
sb.append("hello");
sb.append("world");
sb.append("java");
System.out.println("sb:" + sb);//helloworldjava

/**
 * public StringBuffer replace(int start,int end,String str)
 * 从start开始到end用str替换
 */
// 需求:我要把world这个数据替换为"节日快乐"
sb.replace(5, 10, "节日快乐");
System.out.println("sb:" + sb); //hello节日快乐java

D:反转功能

* public StringBuffer reverse()

// 创建字符串缓冲区对象
StringBuffer sb = new StringBuffer();

// 添加数据
sb.append("霞青林爱我");
System.out.println("sb:" + sb);

// public StringBuffer reverse()
sb.reverse();
System.out.println("sb:" + sb); // 我爱林青霞

E:截取功能(注意这个返回值)

 * StringBuffer的截取功能:注意返回值类型不再是StringBuffer本身了,而是string
 * public String substring(int start)
 * public String substring(int start,int end)

// 创建字符串缓冲区对象
StringBuffer sb = new StringBuffer();

// 添加元素
sb.append("hello").append("world").append("java");
System.out.println("sb:" + sb);

/**
 * public String substring(int start)
 */
String s = sb.substring(5);
System.out.println("s:" + s);
System.out.println("sb:" + sb);

/**
 * public String substring(int start,int end)
 */
String ss = sb.substring(5, 10);
System.out.println("ss:" + ss);
System.out.println("sb:" + sb);

(4)StringBuffer的练习
A:String和StringBuffer相互转换
String -- StringBuffer
构造方法
StringBuffer -- String
toString()方法

// String -- StringBuffer
String s = "hello";
// StringBuffer sb = "hello"; 错误
// StringBuffer sb = s; 错误
/**
 * 给StringBuffer赋值,不能把字符串的值直接赋值给StringBuffer
 * // StringBuffer sb = "hello"; 错误
 * // StringBuffer sb = s; 错误
 * 正确做法:
 * 方式1:通过构造方法
 * 方式2:通过append()方法
 */
// 方式1:通过构造方法
StringBuffer sb = new StringBuffer(s);
// 方式2:通过append()方法
StringBuffer sb2 = new StringBuffer();
sb2.append(s);
System.out.println("sb:" + sb);
System.out.println("sb2:" + sb2);
System.out.println("---------------");

/**
 * StringBuffer转化String
 * 方式1:通过构造方法String(StringBuffer buffer)
 * 方式2:通过toString()方法
 */
StringBuffer buffer = new StringBuffer("java");
// 方式1:通过构造方法
String str = new String(buffer);
// 方式2:通过toString()方法
String str2 = buffer.toString();
System.out.println("str:" + str);
System.out.println("str2:" + str2);

B:字符串的拼接

public static void main(String[] args) {
    // 定义一个数组
    int[] arr = { 44, 33, 55, 11, 22 };

    // 定义功能
    // 方式1:用String做拼接的方式
    String s1 = arrayToString(arr);
    System.out.println("s1:" + s1);

    // 方式2:用StringBuffer做拼接的方式
    String s2 = arrayToString2(arr);
    System.out.println("s2:" + s2);
}

// 用StringBuffer做拼接的方式
public static String arrayToString2(int[] arr) {
    StringBuffer sb = new StringBuffer();

    sb.append("[");
    for (int x = 0; x < arr.length; x++) {
        if (x == arr.length - 1) {
            sb.append(arr[x]);
        } else {
            sb.append(arr[x]).append(", ");
        }
    }
    sb.append("]");

    return sb.toString();
}

// 用String做拼接的方式
public static String arrayToString(int[] arr) {
    String s = "";

    s += "[";
    for (int x = 0; x < arr.length; x++) {
        if (x == arr.length - 1) {
            s += arr[x];
        } else {
            s += arr[x];
            s += ", ";
        }
    }
    s += "]";

    return s;
}

C:把字符串反转

public static void main(String[] args) {
    // 键盘录入数据
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入数据:");
    String s = sc.nextLine();

    // 方式1:用String做拼接
    String s1 = myReverse(s);
    System.out.println("s1:" + s1);
    // 方式2:用StringBuffer的reverse()功能
    String s2 = myReverse2(s);
    System.out.println("s2:" + s2);
}

/**
 * 最佳实践
 */
// 用StringBuffer的reverse()功能
public static String myReverse2(String s) {

    return new StringBuffer(s).reverse().toString();
}

// 用String做拼接
public static String myReverse(String s) {
    String result = "";

    char[] chs = s.toCharArray();
    for (int x = chs.length - 1; x >= 0; x--) {
        // char ch = chs[x];
        // result += ch;
        result += chs[x];
    }

    return result;
}

D:判断一个字符串是否对称

public static void main(String[] args) {
    // 创建键盘录入对象
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入一个字符串:");
    String s = sc.nextLine();

    // 一个一个的比较
    boolean b = isSame(s);
    System.out.println("b:" + b);

    //用字符串缓冲区的反转功能
    boolean b2 = isSame2(s);
    System.out.println("b2:"+b2);
}

/**
 * 最佳实践
 */
public static boolean isSame2(String s) {
    return new StringBuffer(s).reverse().toString().equals(s);
}

public static boolean isSame(String s) {
    boolean flag = true;

    // 把字符串转成字符数组
    char[] chs = s.toCharArray();

    for (int start = 0, end = chs.length - 1; start <= end; start++, end--) {
        if (chs[start] != chs[end]) {
            flag = false;
            break;
        }
    }

    return flag;
}

(5)面试题
小细节:
StringBuffer:同步的,数据安全,效率低。
StringBuilder:不同步的,数据不安全,效率高。

                A:String,StringBuffer,StringBuilder的区别

答: 

* A:String是内容不可变的,而StringBuffer,StringBuilder都是内容可变的。
* B:StringBuffer是同步的,数据安全,效率低;StringBuilder是不同步的,数据不安全,效率高


B:StringBuffer和数组的区别?

答:

 * 二者都可以看出是一个容器,装其他的数据。
 * 但是呢,StringBuffer的数据最终是一个字符串数据。
 * 而数组可以放置多种数据,但必须是同一种数据类型的。


(6)注意的问题:
String作为形式参数,StringBuffer作为形式参数。

答:

 * String作为参数传递。效果和基本类型作为参数传递是一样的。
 * StringBuffer作为参数传递
 *
 * 形式参数:
 * 基本类型:形式参数的改变不影响实际参数
 * 引用类型:形式参数的改变直接影响实际参数

 *
 * 注意:
 * String作为参数传递,效果和基本类型作为参数传递是一样的。
 */

public static void main(String[] args) {
    String s1 = "hello";
    String s2 = "world";
    System.out.println(s1 + "---" + s2);// hello---world
    change(s1, s2);
    // TODO String作为参数传递,效果和基本类型作为参数传递是一样的,不改变原值*******
    System.out.println(s1 + "---" + s2);// hello---world

    StringBuffer sb1 = new StringBuffer("hello");
    StringBuffer sb2 = new StringBuffer("world");
    System.out.println(sb1 + "---" + sb2);// hello---world
    change(sb1, sb2);
    // TODO
    System.out.println(sb1 + "---" + sb2);// hello---worldworld

}

public static void change(String s1, String s2) {
    s1 = s2;
    System.out.println("String Change s1 before:"+s1); // world
    System.out.println("String Change s2 before:"+s2); // world
    s2 = s1 + s2; // 开辟新的地址
    System.out.println("String Change later s1:"+s1); // world
    System.out.println("String Change later s2:"+s2); // worldworld

}

public static void change(StringBuffer sb1, StringBuffer sb2) {
    sb1 = sb2;
    System.out.println("StringBuffer Change s1 before:"+sb1); // world
    System.out.println("StringBuffer Change s2 before:"+sb2); // world
    sb2.append(sb1);

    System.out.println("StringBuffer Change later s1:"+sb1); //worldworld
    System.out.println("StringBuffer Change later s2:"+sb2); //worldworld
}

2:数组高级以及Arrays(掌握)

(1)排序
A:冒泡排序
相邻元素两两比较,大的往后放,第一次完毕,最大值出现在了最大索引处。同理,其他的元素就可以排好。

public static void bubbleSort(int[] arr){
    for (int x = 0; x < arr.length - 1; x++) {
        for (int y = 0; y < arr.length - 1 - x; y++) {
            if (arr[y] > arr[y + 1]) {
                int temp = arr[y];
                arr[y] = arr[y + 1];
                arr[y + 1] = temp;
            }
        }
    }
}		

B:选择排序
把0索引的元素,和索引1以后的元素都进行比较,第一次完毕,最小值出现在了0索引。同理,其他的元素就可以排好。

public static void selectSort(int[] arr){
    for(int x=0; x<arr.length-1; x++){
        for(int y=x+1; y<arr.length; y++){
            if(arr[y] <arr[x]){
                int temp = arr[x];
                arr[x] = arr[y];
                arr[y] = temp;
            }
        }
    }
}					

(2)查找
A:基本查找
针对数组无序的情况

public static int getIndex(int[] arr,int value) {
int index = -1;

for(int x=0; x<arr.length; x++) {
if(arr[x] == value) {
index = x;
break;
}
}

return index;
}
B:二分查找(折半查找)
针对数组有序的情况(千万不要先排序,在查找)

public static int getIndex(int[] arr,int value){
    //定义最大索引,最小索引
    int max = arr.length -1;
    int min = 0;

    //计算出中间索引
    int mid = (max +min)/2;

    //拿中间索引的值和要查找的值进行比较
    while(arr[mid] != value){
        if(arr[mid]>value){
            max = mid - 1;
        }else if(arr[mid]<value){
            min = mid + 1;
        }

        //加入判断
        if(min > max){
            return -1;
        }

        mid = (max +min)/2;
    }

    return mid;
}

(3)Arrays工具类
A:是针对数组进行操作的工具类。包括排序查找等功能。
B:要掌握的方法(自己补齐方法)

 * 1:public static String toString(int[] a)                    把数组转成字符串
 * 2:public static void sort(int[] a)                              对数组进行排序
 * 3:public static int binarySearch(int[] a,int key)      二分查找

// 定义一个数组
int[] arr = { 24, 69, 80, 57, 13 };

/**
 * public static String toString(int[] a)
 * 把数组转成字符串
 */
System.out.println("排序前:" + Arrays.toString(arr));

/**
 * public static void sort(int[] a)
 * 对数组进行排序
 */
Arrays.sort(arr);
System.out.println("排序后:" + Arrays.toString(arr));

/**
 * public static int binarySearch(int[] a,int key)
 * 二分查找
 */
// [13, 24, 57, 69, 80]
System.out.println("binarySearch:" + Arrays.binarySearch(arr, 57)); // 2
System.out.println("binarySearch:" + Arrays.binarySearch(arr, 577));

(4)Arrays工具类的源码解析
(5)把字符串中的字符进行排序
举例:
"edacbgf"
得到结果
"abcdefg"
 

3:Integer(掌握)

(1)为了让基本类型的数据进行更多的操作,Java就为每种基本类型提供了对应的包装类类型
byte          Byte
short         Short
int             Integer
long          Long
float          Float
double      Double
char          Character
boolean    Boolean
(2)Integer的构造方法
A:Integer i = new Integer(100);
B:Integer i = new Integer("100");
注意:这里的字符串必须是由数字字符组成

/**
 * public Integer(int value)
 */
int i = 100;
Integer ii = new Integer(i);
System.out.println("ii:" + ii);

/**
 * public Integer(String s)
 */
String s = "100";
// NumberFormatException
// String s = "abc";
Integer iii = new Integer(s);
System.out.println("iii:" + iii);

(3)String和int的相互转换
A:String -- int
Integer.parseInt("100");
B:int -- String
String.valueOf(100);
(4)其他的功能(了解)
进制转换

// 十进制到二进制,八进制,十六进制
System.out.println(Integer.toBinaryString(100));
System.out.println(Integer.toOctalString(100));
System.out.println(Integer.toHexString(100));

(5)JDK5的新特性
自动装箱 基本类型--引用类型
自动拆箱 引用类型--基本类型

把下面的这个代码理解即可:
Integer i = 100;
i += 200;
(6)面试题
-128到127之间的数据缓冲池问题

                针对-128到127之间的数据,做了一个数据缓冲池,如果数据是该范围内的,每次并不创建新的空间

 

4:Character(了解)

(1)Character构造方法
Character ch = new Character('a');
(2)要掌握的方法:

 * public static boolean isUpperCase(char ch)           判断给定的字符是否是大写字符
 * public static boolean isLowerCase(char ch)            判断给定的字符是否是小写字符
 * public static boolean isDigit(char ch)                     判断给定的字符是否是数字字符
 * public static char toUpperCase(char ch)                 把给定的字符转换为大写字符
 * public static char toLowerCase(char ch)                 把给定的字符转换为小写字符
(3)案例:
统计字符串中大写,小写及数字字符出现的次数

// 定义三个统计变量。
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:正则表达式(理解)

(1)就是符合一定规则的字符串
(2)常见规则
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 次 
(3)常见功能:(分别用的是谁呢?)
A:判断功能
String类的public boolean matches(String regex)
B:分割功能
String类的public String[] split(String regex)

        //定义一个年龄搜索范围
        String ages = "18-24";

        //定义规则
        String regex = "-";

        //调用方法
        String[] strArray = ages.split(regex);

        //如何得到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");
        }

C:替换功能
String类的public String replaceAll(String regex,String replacement)

// 定义一个字符串
String s = "helloqq12345worldkh622112345678java";

// 直接把数字干掉
String regex = "\\d+";
String ss = "";

String result = s.replaceAll(regex, ss);
System.out.println(result); // helloqqworldkhjava

D:获取功能
Pattern和Matcher
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");

find():查找存不存在
group():获取刚才查找过的数据

(4)案例
A:判断电话号码和邮箱
B:按照不同的规则分割数据
C:把论坛中的数字替换为*
D:获取字符串中由3个字符组成的单词

2:Math(掌握)

(1)针对数学运算进行操作的类
(2)常见方法(自己补齐)

 * 成员变量:
 * 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):最大值 (min自学)
 * public static double pow(double a,double b):a的b次幂
 * public static double random():随机数 [0.0,1.0)
 * public static int round(float a) 四舍五入(参数为double的自学)
 * public static double sqrt(double a):正平方根

(3)案例:
A:猜数字小游戏
B:获取任意范围的随机数

3:Random(理解)

(1)用于产生随机数的类
(2)构造方法:
A:Random() 默认种子,每次产生的随机数不同
B:Random(long seed) 指定种子,每次种子相同,随机数就相同
(3)成员方法:
A:int nextInt() 返回int范围内的随机数
B:int nextInt(int n) 返回[0,n)范围内的随机数
 

4:System(掌握)

(1)系统类,提供了一些有用的字段和方法,它不能被实例化。


      成员方法:
 * public static void gc():                                        运行垃圾回收器。

Person p = new Person("笑嘻嘻", 60);
System.out.println(p);

p = null; // 让p不再指定堆内存
System.gc();

 * public static void exit(int status) 退出jvm。         终止当前正在运行的 Java 虚拟机。参数用作状态码;根据惯                                                                                                     例, 非 0  的状态码表示异常终止。 

System.out.println("我们喜欢林青霞(东方不败)");
System.exit(0);
System.out.println("我们也喜欢赵雅芝(白娘子)"); // 不执行

 * public static long currentTimeMillis()                   获取当前时间的毫秒值

System.out.println(System.currentTimeMillis()); // 1528449538842

// 单独得到这样的实际目前对我们来说意义不大
// 那么,它到底有什么作用呢?
// 要求:请大家给我统计这段程序的运行时间
long start = System.currentTimeMillis();
for (int x = 0; x < 100; x++) {
    System.out.println("hello" + x);
}
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "毫秒"); // 共耗时:1毫秒

 * public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)    数组复制。

                                                     从指定源数 组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。

// 定义数组
int[] arr = { 11, 22, 33, 44, 55 };
int[] arr2 = { 6, 7, 8, 9, 10 };

// 请大家看这个代码的意思
System.arraycopy(arr, 1, arr2, 2, 2);

System.out.println(Arrays.toString(arr)); // [11, 22, 33, 44, 55]
System.out.println(Arrays.toString(arr2)); // [6, 7, 22, 33, 10]

 

5:BigInteger(理解)

(1)针对大整数的运算
(2)构造方法
A:BigInteger(String s)
(3)成员方法(自己补齐)
 * public BigInteger add(BigInteger val):            加
 * public BigInteger subtract(BigInteger val):     减
 * public BigInteger multiply(BigInteger val):     乘
 * public BigInteger divide(BigInteger val):         除
 * public BigInteger[] divideAndRemainder(BigInteger val):        返回商和余数的数组

 

BigInteger bi1 = new BigInteger("100");
BigInteger bi2 = new BigInteger("50");

System.out.println("add:" + bi1.add(bi2));

System.out.println("subtract:" + bi1.subtract(bi2));

System.out.println("multiply:" + bi1.multiply(bi2));

System.out.println("divide:" + bi1.divide(bi2));

// public BigInteger[] divideAndRemainder(BigInteger val):返回商和余数的数组
BigInteger[] bis = bi1.divideAndRemainder(bi2);
System.out.println("商:" + bis[0]);
System.out.println("余数:" + bis[1]);

6:BigDecimal(理解)

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

* 看程序写结果:结果和我们想的有一点点不一样,这是因为float类型的数据存储和整数不一样导致的。它们大部分的时候,都是带有有效数字位。
 * 由于在运算的时候,float类型和double很容易丢失精度,演示案例。所以,为了能精确的表示、计算浮点数,Java提供了BigDecimal
 * BigDecimal类:不可变的、任意精度的有符号十进制数,可以解决数据丢失问题。

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

(2)构造方法
A:BigDecimal(String s)

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

(3)成员方法:


 * public BigDecimal add(BigDecimal augend)
 * public BigDecimal subtract(BigDecimal subtrahend)
 * public BigDecimal multiply(BigDecimal multiplicand)
 * public BigDecimal divide(BigDecimal divisor)
 * public BigDecimal divide(BigDecimal divisor,int scale,int roundingMode):商,几位小数,如何舍取

 

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

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

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

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

7:Date/DateFormat(掌握)

(1)Date是日期类,可以精确到毫秒。
A:构造方法
Date()
Date(long time)

// 创建对象
Date d = new Date();
System.out.println("d:" + d); // Mon Jun 11 10:12:23 CST 2018

// 创建对象
// long time = System.currentTimeMillis();
long time = 1000 * 60 * 60; // 1小时
// TODO 时间???**************
Date d2 = new Date(time);
System.out.println("d2:" + d2); // Thu Jan 01 09:00:00 CST 1970

B:成员方法
* public long getTime():获取时间,以毫秒为单位 和System.currentTimeMillis()得到的一样
 * public void setTime(long time):设置时间
 
 * 从Date得到一个毫秒值
   getTime()
 * 把一个毫秒值转换为Date
   构造方法
   setTime(long time)

// 创建对象
Date d = new Date();

// 获取时间
long time = d.getTime();
System.out.println(time); // 1528688513055
System.out.println(System.currentTimeMillis()); // 1528688513056

System.out.println("d:" + d); // Mon Jun 11 11:41:53 CST 2018
// 设置时间 1970-01-01 8:00:00 基础上加
d.setTime(1000);
System.out.println("d:" + d); // Thu Jan 01 08:00:01 CST 1970

C:日期和毫秒值的相互转换
案例:你来到这个世界多少天了?

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

 *  年 y
 *  月 M
 *  日 d
 *  时 H
 *  分 m
 *  秒 s
A: SimpleDateFormat的构造方法

             *  SimpleDateFormat():默认模式 18-6-11 上午11:45
             *  SimpleDateFormat(String pattern):给定的模式   yyyy-MM-dd HH:mm:ss 

                                             SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
B:日期和字符串的转换 

                        SimpleDateFormat的对象进行调用
a:Date --> String
public final String format(Date date)

b:String --> Date
public Date parse(String source)

/**
 * Date --> String
 */
Date d = new Date();
// 创建格式化对象
// 默认模式
// SimpleDateFormat sdf = new SimpleDateFormat(); // 18-6-11 上午11:45
// 给定模式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
// public final String format(Date date)
String s = sdf.format(d);
System.out.println(s); // 2018年06月11日 11:50:57

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

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

/**
 * 这是日期和字符串相互转换的工具类
 */
public class Date4Util {

    // TODO 为什么要有个无参构造************
    private Date4Util() {
    }

    /**
     * 这个方法的作用就是把日期转成一个字符串
     *
     * @param d
     *            被转换的日期对象
     * @param format
     *            传递过来的要被转换的格式
     * @return 格式化后的字符串
     */
    public static String dateToString(Date d, String format) {
        return new SimpleDateFormat(format).format(d);
    }

    /**
     * 这个方法的作用就是把一个字符串解析成一个日期对象
     *
     * @param s
     *            被解析的字符串
     * @param format
     *            传递过来的要被转换的格式
     * @return 解析后的日期对象
     * @throws ParseException
     */
    public static Date stringToDate(String s, String format) throws ParseException {
        return new SimpleDateFormat(format).parse(s);
    }
}

测试类:

 

Date d = new Date();
// yyyy-MM-dd HH:mm:ss
String s = Date4Util.dateToString(d, "yyyy年MM月dd日 HH:mm:ss");
System.out.println(s); // 2018年06月11日 12:15:24

String s2 = Date4Util.dateToString(d, "yyyy年MM月dd日");
System.out.println(s2); // 2018年06月11日

String s3 = Date4Util.dateToString(d, "HH:mm:ss");
System.out.println(s3); // 12:15:24

String str = "2014-10-14";
Date dd = Date4Util.stringToDate(str, "yyyy-MM-dd");
System.out.println(dd); // Tue Oct 14 00:00:00 CST 2014

8:Calendar(掌握)

(1)日历类,封装了所有的日历字段值(YEAR、MONTH、DAY_OF_MONTH、HOUR 等),通过统一的方法根据传入不同的日历字段可以获取值。日历类中的每个日历字段都是静态的成员变量,并且是int类型。

(2)如何得到一个日历对象呢?
Calendar rightNow = Calendar.getInstance();
本质返回的是子类对象
(3)成员方法
A:根据日历字段得到对应的值 public int get(int field)

/**
 * 获取当前的日历时间
 */
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 + "日");

B:根据日历字段和一个正负数确定是添加还是减去对应日历字段的值 public void add(int field,int amount)

/**
 * 三年前的今天
 */
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 + "日");

/**
 * 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 + "日");
System.out.println("--------------");

C:设置日历对象的年月日 public final void set(int year,int month,int date)

/**
 * 设置日历
 */
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 + "日");

(4)案例:
计算任意一年的2月份有多少天?

// 键盘录入任意的年份
Scanner sc = new Scanner(System.in);
System.out.println("请输入年份:");
int year = sc.nextInt();

// 设置日历对象的年月日
Calendar c = Calendar.getInstance();
c.set(year, 2, 1); // 其实是这一年的3月1日
// 把时间往前推一天,就是2月的最后一天
c.add(Calendar.DATE, -1);

// 获取这一天输出即可
System.out.println(c.get(Calendar.DATE));

============================================================================

1:对象数组(掌握)

(1)数组既可以存储基本数据类型,也可以存储引用类型。它存储引用类型的时候的数组就叫对象数组。
(2)案例:
用数组存储5个学生对象,并遍历数组。

// 创建学生数组(对象数组)。
Student[] students = new Student[5];

// 创建5个学生对象,并赋值。
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("风清扬", 30);
Student s3 = new Student("刘意", 30);
Student s4 = new Student("赵雅芝", 60);
Student s5 = new Student("王力宏", 35);

// 把C步骤的元素,放到数组中。
students[0] = s1;
students[1] = s2;
students[2] = s3;
students[3] = s4;
students[4] = s5;

// 遍历
for (int x = 0; x < students.length; x++) {
    Student s = students[x];
    System.out.println(s.getName()+"---"+s.getAge());
}

2:集合(Collection)(掌握)

(1)集合的由来?
我们学习的是Java -- 面向对象 -- 操作很多对象 -- 存储 -- 容器(数组和StringBuffer) -- 数组
而数组的长度固定,所以不适合做变化的需求,Java就提供了集合供我们使用。

(2)集合和数组的区别?
A:长度区别
数组固定
集合可变
B:内容区别
数组可以是基本类型,也可以是引用类型。
集合只能是引用类型。
C:元素内容
数组只能存储同一种类型
集合可以存储不同类型(其实集合一般存储的也是同一种类型)
(3)集合的继承体系结构?
由于需求不同,Java就提供了不同的集合类。这多个集合类的数据结构不同,但是它们都是要提供存储和遍历功能的,
我们把它们的共性不断的向上提取,最终就形成了集合的继承体系结构图。

Collection
|--List
  |--ArrayList
  |--Vector
  |--LinkedList
|--Set
  |--HashSet
  |--TreeSet

(4)Collection的功能概述(自己补齐)
A:添加功能

*  boolean add(Object obj):添加一个元素
 * boolean addAll(Collection c):添加一个集合的元素

/**
 * boolean add(Object obj)
 * 添加一个元素
 */
c.add("hello");
c.add("world");
c.add("java");


/**
 * boolean addAll(Collection c)
 * 添加一个集合的元素
 */
Collection c1 = new ArrayList();
c1.add("嘻嘻");
c.addAll(c1);

B:删除功能

 * void clear():移除所有元素
 * boolean remove(Object o):移除一个元素
 * boolean removeAll(Collection c):移除一个集合的元素(是一个还是所有)

/**
 * void clear()
 * 移除所有元素
 */
c.clear(); // []

/**
 * boolean remove(Object o)
 * 移除一个元素
 */
System.out.println("remove:" + c.remove("hello"));
System.out.println("remove:" + c.remove("嘻嘻"));
System.out.println("removeAll:" + c.removeAll(c1));

C:判断功能

*  boolean contains(Object o):判断集合中是否包含指定的元素
 * boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(是一个还是所有)
 * boolean isEmpty():判断集合是否为空

/**
 * boolean contains(Object o)
 * 判断集合中是否包含指定的元素
 */
System.out.println("contains:"+c.contains("hello"));
System.out.println("contains:"+c.contains("android"));

/**
 * boolean isEmpty()
 * 判断集合是否为空
 */
System.out.println("isEmpty:"+c.isEmpty());

D:获取功能

                        Iterator<E> iterator()(重点)
E:长度功能

                        int size():元素的个数

 *         面试题:数组有没有length()方法呢?字符串有没有length()方法呢?集合有没有length()方法呢?

/**
 * int size()
 * 元素的个数
 */
System.out.println("size:"+c.size());
System.out.println("c:" + c);

F:交集(了解)

                        boolean retainAll(Collection c):两个集合都有的元素?思考元素去哪了,返回的boolean又是什么意思呢?
G:把集合转数组(了解)

                        Object[] toArray()
(5)Collection集合的遍历
A:把集合转数组(了解)

        // 创建集合对象
        Collection c = new ArrayList();

        // 创建学生对象
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("风清扬", 30);
        Student s3 = new Student("令狐冲", 33);
        Student s4 = new Student("武鑫", 25);
        Student s5 = new Student("刘晓曲", 22);

        // 把学生添加到集合
        c.add(s1);
        c.add(s2);
        c.add(s3);
        c.add(s4);
        c.add(s5);

        System.out.println(c);
        // [com.shiki.common.basic.collection.Student@1d44bcfa,
        // com.shiki.common.basic.collection.Student@266474c2,
        // com.shiki.common.basic.collection.Student@6f94fa3e,
        // com.shiki.common.basic.collection.Student@5e481248,
        // com.shiki.common.basic.collection.Student@66d3c617]

        // 把集合转成数组
        Object[] objs = c.toArray();
        System.out.println(objs); // [Ljava.lang.Object;@63947c6b

        // 遍历数组
        for (int x = 0; x < objs.length; x++) {
//          System.out.println(objs[x]); com.shiki.common.basic.collection.Student@1d44bcfa
            Student s = (Student) objs[x];
            System.out.println(s.getName() + "---" + s.getAge());
        }

B:迭代器(集合专用方式)

 *         Iterator iterator():迭代器,集合的专用遍历方式 Iterator it = c.iterator(); 
 *         Object next():获取元素,并移动到下一个位置。
 * NoSuchElementException:没有这样的元素,因为你已经找到最后了。
 *         boolean hasNext():如果仍有元素可以迭代,则返回 true。

// 创建集合对象
Collection c = new ArrayList();

// 创建并添加元素
c.add("hello");
c.add("world");
c.add("java");

/**
 * Iterator iterator()
 * 迭代器,集合的专用遍历方式
 */
Iterator it = c.iterator(); // 实际返回的肯定是子类对象,这里是多态

// 最终版代码
while (it.hasNext()) {
// System.out.println(it.next()); 放开报错:NoSuchElementException
   String s = (String) it.next();
   System.out.println(s);
}

String s = (String) it.next(); System.out.println(s);} (6)迭代器
A:是集合的获取元素的方式。
B:是依赖于集合而存在的。
C:迭代器的原理和源码。
a:为什么定义为了一个接口而不是实现类?
b:看了看迭代器的内部类实现。

(7)Collection集合的案例(遍历方式 迭代器)
集合的操作步骤:
A:创建集合对象
B:创建元素对象
C:把元素添加到集合
D:遍历集合

A:存储字符串并遍历

// 创建集合对象
Collection c = new ArrayList();

// 把字符串对象添加到集合中
c.add("林青霞");
c.add("风清扬");
c.add("刘意");
c.add("武鑫");
c.add("刘晓曲");

// 遍历集合
// 通过集合对象获取迭代器对象
Iterator it = c.iterator();
// 通过迭代器对象的hasNext()方法判断有没有元素
while (it.hasNext()) {
    // 通过迭代器对象的next()方法获取元素
    String s = (String) it.next();
    System.out.println(s);
}	

B:存储自定义对象并遍历
public class Student {
private String name;
private int age;

public Student(){}

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

//getXxx()/setXxx()
}

import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;

public class StudentDemo {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();

//创建学生对象
Student s1 = new Student("林青霞",27);
Student s2 = new Student("风清扬",30);
Student s3 = new Student("刘意",30);
Student s4 = new Student("武鑫",25);
Student s5 = new Student("刘晓曲",16);

//添加元素
c.add(s1);
c.add(s2);
c.add(s3);
c.add(s4);
c.add(s5);

//遍历集合
Iterator it = c.iterator();
while(it.hasNext()) {
Student s = (Student)it.next();
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
 

3:集合(List)(掌握)

(1)List是Collection的子接口
特点:有序(存储顺序和取出顺序一致),可重复
(2)List的特有功能:(自己补齐)
 * A:添加功能
 * void add(int index,Object element):在指定位置添加元素
 * B:获取功能
 * Object get(int index):获取指定位置的元素
 * C:列表迭代器
 * ListIterator listIterator():List集合特有的迭代器
 * D:删除功能
 * Object remove(int index):根据索引删除元素,返回被删除的元素
 * E:修改功能
 * Object set(int index,Object element):根据索引修改元素,返回被修饰的元素

System.out.println("list:" + list);
/**
 * void add(int index,Object element)
 * 在指定位置添加元素
 */
 list.add(1, "android");//没有问题
System.out.println("list:" + list);
// Object get(int index):获取指定位置的元素
// System.out.println("get:" + list.get(1));
// IndexOutOfBoundsException
// System.out.println("get:" + list.get(11));

/**
 * Object remove(int index)
 * 根据索引删除元素,返回被删除的元素
 */
System.out.println("remove:" + list.remove(1));
// IndexOutOfBoundsException
// System.out.println("remove:" + list.remove(11));

/**
 * Object set(int index,Object element)
 * 根据索引修改元素,返回被修改的元素
 */
System.out.println("set:" + list.set(1, "javaee"));

System.out.println("list:" + list);

(3)List集合的特有遍历功能
A:由size()和get()结合。
B:代码演示

// 创建集合对象
List list = new ArrayList();

// 创建学生对象
Student s1 = new Student("林黛玉", 18);
Student s2 = new Student("刘姥姥", 88);
Student s3 = new Student("王熙凤", 38);

// 把学生添加到集合中
list.add(s1);
list.add(s2);
list.add(s3);

// 遍历
// 迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
    Student s = (Student) it.next();
    System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("--------");

// 普通for循环
for (int x = 0; x < list.size(); x++) {
    Student s = (Student) list.get(x);
    System.out.println(s.getName() + "---" + s.getAge());
}

(4)列表迭代器的特有功能;(了解)
可以逆向遍历,但是要先正向遍历,所以无意义,基本不使用。

// 创建List集合对象
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add("java");

        // ListIterator listIterator()
        ListIterator lit = list.listIterator(); // 子类对象
         while (lit.hasNext()) {
         String s = (String) lit.next();
         System.out.println(s);
         }
         System.out.println("-----------------");

        /**
         * Object previous()
         * 获取上一个元素
         */
//        System.out.println(lit.previous());
//         System.out.println(lit.previous());
//         System.out.println(lit.previous());
        // NoSuchElementException
        // System.out.println(lit.previous());
        System.out.println("while:-----------------");
        while (lit.hasPrevious()) {
            String s = (String) lit.previous();
            System.out.println(s);
        }
        System.out.println("-----------------");

        // 迭代器
        Iterator it = list.iterator();
        while (it.hasNext()) {
            String s = (String) it.next();
            System.out.println(s);
        }
        System.out.println("-----------------");

(5)并发修改异常
A:出现的现象
迭代器遍历集合,集合修改集合元素
B:原因
迭代器是依赖于集合的,而集合的改变迭代器并不知道。
C:解决方案

// 创建List集合对象
List list = new ArrayList();
// 添加元素
list.add("hello");
list.add("world");
list.add("java");
System.out.println(list); // [hello, world, java]

a:迭代器遍历,迭代器修改(ListIterator)
元素添加在刚才迭代的位置

ListIterator lit = list.listIterator();
while (lit.hasNext()) {
    String s = (String) lit.next();
    if ("world".equals(s)) {
    lit.add("javaee");
    }
} // [hello, world, javaee, java]

b:集合遍历,集合修改(size()和get())
元素添加在集合的末尾

for (int x = 0; x < list.size(); x++) {
    String s = (String) list.get(x);
    if ("world".equals(s)) {
        list.add("javaee");
    }
} //[hello, world, java, javaee]


(6)常见数据结构
A:栈 先进后出
B:队列 先进先出
C:数组 查询快,增删慢
D:链表 查询慢,增删快

(7)List的子类特点(面试题)
ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。
Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。
LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。


到底使用谁呢?看需求?
分析:
要安全吗?
要:Vector(即使要,也不使用这个,后面再说)
不要:ArrayList或者LinkedList
查询多;ArrayList
增删多:LinkedList


什么都不知道,就用ArrayList。
(8)List集合的案例(遍历方式 迭代器和普通for)
A:存储字符串并遍历
B:存储自定义对象并遍历

============================================================================

 

1:List的子类(掌握)

(1)List的子类特点
ArrayList:
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
Vector:
底层数据结构是数组,查询快,增删慢
线程安全,效率低
LinkedList:
底层数据结构是链表,查询慢,增删快
线程不安全,效率高

(2)ArrayList
A:没有特有功能需要学习
B:案例
a:ArrayList存储字符串并遍历

b:ArrayList存储自定义对象并遍历

 

// 创建集合对象
ArrayList array = new ArrayList();

// 创建学生对象
Student s1 = new Student("武松", 30);
Student s2 = new Student("鲁智深", 40);
Student s3 = new Student("林冲", 36);
Student s4 = new Student("杨志", 38);

// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);

// 遍历
Iterator it = array.iterator();
while (it.hasNext()) {
    Student s = (Student) it.next();
    System.out.println(s.getName() + "---" + s.getAge());
}

System.out.println("----------------");

for (int x = 0; x < array.size(); x++) {
    // ClassCastException 注意,千万要搞清楚类型
    // String s = (String) array.get(x);
    // System.out.println(s);

    Student s = (Student) array.get(x);
    System.out.println(s.getName() + "---" + s.getAge());
}

(3)Vector
A:有特有功能
* 1:添加功能
 * public void addElement(Object obj) -- add()

 * 2:获取功能
 * public Object elementAt(int index) --  get()
 * public Enumeration elements() -- Iterator iterator()
 * boolean hasMoreElements() hasNext()
 * Object nextElement() next()
B:案例
a:Vector存储字符串并遍历

b:Vector存储自定义对象并遍历

(4)LinkedList
A:有特有功能
        A:添加功能
 *     public void addFirst(Object e)
 *     public void addLast(Object e)
 *         B:获取功能
 *     public Object getFirst()
 *     public Obejct getLast()
 *         C:删除功能
 *     public Object removeFirst()
 *     public Object removeLast()
B:案例
a:LinkedList存储字符串并遍历

b:LinkedList存储自定义对象并遍历

// 创建集合对象
LinkedList link = new LinkedList();

// 添加元素
link.add("hello");
link.add("world");
link.add("java");
System.out.println("link:" + link);

/**
 * public void addFirst(Object e)
 */
// 
 link.addFirst("javaee");
/**
 * public void addLast(Object e)
 */
 link.addLast("android");
System.out.println("link:" + link);

/**
 * public Object getFirst()
 */
// 
 System.out.println("getFirst:" + link.getFirst());
/**
 * public Obejct getLast()
 */
 System.out.println("getLast:" + link.getLast());

/**
 * public Object removeFirst()
 */
System.out.println("removeFirst:" + link.removeFirst());
/**
 * public Object removeLast()
 */
System.out.println("removeLast:" + link.removeLast());

// 输出对象名
System.out.println("link:" + link);

(5)案例:
A:去除集合中的多个字符串的重复元素

如果字符串的内容相同,即为重复元素

// 创建集合对象
ArrayList array = new ArrayList();

// 添加多个字符串元素(包含内容相同的)
array.add("hello");
array.add("world");
array.add("java");
array.add("world");
array.add("java");
array.add("world");
array.add("world");
array.add("world");
array.add("world");
array.add("java");
array.add("world");

// 由选择排序思想引入,我们就可以通过这种思想做这个题目
// 拿0索引的依次和后面的比较,有就把后的干掉
// 同理,拿1索引...
for (int x = 0; x < array.size() - 1; x++) {
    for (int y = x + 1; y < array.size(); y++) {
        if (array.get(x).equals(array.get(y))) {
            array.remove(y);
            y--;
        }
    }
}

// 遍历集合
Iterator it = array.iterator();
while (it.hasNext()) {
    String s = (String) it.next();
    System.out.println(s);
}

B:去除集合中的多个自定义对象的重复元素

如果自定义对象的成员变量值都相同,即为重复元素

// 创建集合对象
ArrayList array = new ArrayList();

// 创建学生对象
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("林志玲", 40);
Student s3 = new Student("凤姐", 35);
Student s4 = new Student("芙蓉姐姐", 18);
Student s5 = new Student("翠花", 16);
Student s6 = new Student("林青霞", 27);
Student s7 = new Student("林青霞", 18);

// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);
array.add(s6);
array.add(s7);

// 创建新集合
ArrayList newArray = new ArrayList();

// 遍历旧集合,获取得到每一个元素
Iterator it = array.iterator();
while (it.hasNext()) {
    Student s = (Student) it.next();

    // 拿这个元素到新集合去找,看有没有
    if (!newArray.contains(s)) {
        newArray.add(s);
    }
}

// 遍历新集合
for (int x = 0; x < newArray.size(); x++) {
    Student s = (Student) newArray.get(x);
    System.out.println(s.getName() + "---" + s.getAge());
}

 

C:用LinkedList模拟一个栈数据结构的集合类,并测试。

你要定义一个集合类,只不过内部可以使用LinkedList来实现。

// A: LinkedList的特有添加功能addFirst()
// B:栈的特点先进后出
// 创建集合对象
 LinkedList link = new LinkedList();

 // 添加元素
 link.addFirst("hello");
 link.addFirst("world");
 link.addFirst("java");

 // 遍历
 Iterator it = link.iterator();
 while (it.hasNext()) {
 String s = (String) it.next();
 System.out.println(s);
 }
 // 先进后出
 // java
 // world
 // hello

 

2:泛型(掌握)

(1)泛型概述
是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。
(2)格式:
<数据类型>
注意:该数据类型只能是引用类型
(3)好处:
A:把运行时期的问题提前到了编译期间
B:避免了强制类型转换
C:优化了程序设计,解决了黄色警告线问题,让程序更安全
(4)泛型的前世今生
A:泛型的由来
Object类型作为任意类型的时候,在向下转型的时候,会隐含一个转型问题

B:泛型类

public class ObjectToolClass<T> {
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}
ObjectToolClass<String> ot = new ObjectToolClass<String>();
// ot.setObj(new Integer(27)); //这个时候编译期间就过不去
ot.setObj(new String("林青霞"));
String s = ot.getObj();
System.out.println("姓名是:" + s);

ObjectToolClass<Integer> ot2 = new ObjectToolClass<Integer>();
// ot2.setObj(new String("风清扬"));//这个时候编译期间就过不去
ot2.setObj(new Integer(27));
Integer i = ot2.getObj();
System.out.println("年龄是:" + i);

C:泛型方法

public class ObjectToolMethod {
    public <T> void show(T t) {
        System.out.println(t);
    }
}

 

// 如果还听得懂,那就说明泛型类是没有问题的
// 但是呢,谁说了我的方法一定要和类的类型的一致呢?
// 我要是类上没有泛型的话,方法还能不能接收任意类型的参数了呢?

// 定义泛型方法后
ObjectToolMethod ot = new ObjectToolMethod();
ot.show("hello");
ot.show(100);
ot.show(true);

D:泛型接口

public interface ObjectToolInterface<T> {
    public abstract void show(T t);
}
public class ObjectToolInterfaceImpl<T>  implements ObjectToolInterface<T> {

    @Override
    public void show(T t) {
        System.out.println(t);
    }
}
// 第一种情况的测试
// Inter<String> i = new InterImpl();
// i.show("hello");

// // 第二种情况的测试
ObjectToolInterface<String> i = new ObjectToolInterfaceImpl<String>();
i.show("hello");

ObjectToolInterface<Integer> ii = new ObjectToolInterfaceImpl<Integer>();
ii.show(100);

E:泛型高级通配符
 
 * ?:任意类型,如果没有明确,那么就是Object以及任意的Java类了
 * ? extends E:向下限定,E及其子类

 * ? super E:向上限定,E及其父类

public class GenericAdvanced {
    public static void main(String[] args) {
        // 泛型如果明确的写的时候,前后必须一致
        Collection<Object> c1 = new ArrayList<Object>();
        // 以下为错误示范:
        // Collection<Object> c2 = new ArrayList<Animal>();
        // Collection<Object> c3 = new ArrayList<Dog>();
        // Collection<Object> c4 = new ArrayList<Cat>();

        // ?表示任意的类型都是可以的
        Collection<?> c5 = new ArrayList<Object>();
        Collection<?> c6 = new ArrayList<Animal>();
        Collection<?> c7 = new ArrayList<Dog>();
        Collection<?> c8 = new ArrayList<Cat>();

        // ? extends E:向下限定,E及其子类
        // Collection<? extends Animal> c9 = new ArrayList<Object>();
        Collection<? extends Animal> c10 = new ArrayList<Animal>();
        Collection<? extends Animal> c11 = new ArrayList<Dog>();
        Collection<? extends Animal> c12 = new ArrayList<Cat>();

        // ? super E:向上限定,E极其父类
        Collection<? super Animal> c13 = new ArrayList<Object>();
        Collection<? super Animal> c14 = new ArrayList<Animal>();
        // Collection<? super Animal> c15 = new ArrayList<Dog>();
        // Collection<? super Animal> c16 = new ArrayList<Cat>();
    }
}

class Animal {
}

class Dog extends Animal {
}

class Cat extends Animal {
}

(5)我们在哪里使用呢?
一般是在集合中使用。

 

3:增强for循环(掌握)

(1)是for循环的一种
(2)格式:
for(元素的数据类型 变量名 : 数组或者Collection集合的对象) {
使用该变量即可,该变量其实就是数组或者集合中的元素。
}
(3)好处:
简化了数组和集合的遍历
(4)弊端
增强for循环的目标不能为null。建议在使用前,先判断是否为null。

// 创建集合对象
ArrayList<Student> array = new ArrayList<Student>();

// 创建学生对象
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("貂蝉", 22);
Student s3 = new Student("杨玉环", 24);
Student s4 = new Student("西施", 21);
Student s5 = new Student("王昭君", 23);

// 把学生对象添加到集合中
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);

// 迭代器
Iterator<Student> it = array.iterator();
while (it.hasNext()) {
    Student s = it.next();
    System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("---------------");

// 普通for
for (int x = 0; x < array.size(); x++) {
    Student s = array.get(x);
    System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("---------------");

// 增强for
for (Student s : array) {
    System.out.println(s.getName() + "---" + s.getAge());
}

 

4:静态导入(了解)

(1)可以导入到方法级别的导入
(2)格式:
import static 包名....类名.方法名;
(3)注意事项:
A:方法必须是静态
B:如果多个类下有同名的方法,就不好区分了,还得加上前缀
所以一般我们并不使用静态导入,但是一定要能够看懂。

import static java.lang.Math.max;
import static java.lang.Math.pow;

/*
 * 静态导入:
 * 格式:import static 包名….类名.方法名;
 * 可以直接导入到方法的级别
 *
 * 静态导入的注意事项:
 *        A:方法必须是静态的
 *        B:如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀。由此可见,意义不大,所以一般不用,但是要能看懂。
 */
public class StaticImport {
    public static void main(String[] args) {
        // System.out.println(java.lang.Math.abs(-100));
        // System.out.println(java.lang.Math.pow(2, 3));
        // System.out.println(java.lang.Math.max(20, 30));
        // 太复杂,我们就引入到import

        System.out.println(Math.abs(-100));
        System.out.println(Math.pow(2, 3));
        System.out.println(Math.max(20, 30));
        // 太复杂,有更简单

//    System.out.println(abs(-100));
        System.out.println(java.lang.Math.abs(-100));
        System.out.println(pow(2, 3));
        System.out.println(max(20, 30));
    }

    public static void abs(String s){
        System.out.println(s);
    }
}

5:可变参数(掌握)

(1)如果我们在写方法的时候,参数个数不明确,就应该定义可变参数
(2)格式:
修饰符 返回值类型 方法名(数据类型... 变量) {}

注意:
A:该变量其实是一个数组名

B:如果一个方法有多个参数,并且有可变参数,可变参数必须在最后

public static void main(String[] args) {
    // 4个数据的求和
    int d = 30;
    result = sum(a, b, c, d);
    System.out.println("result:" + result);

    // 需求:我要写一个求和的功能,到底是几个数据求和呢,我不太清楚,但是我知道在调用的时候我肯定就知道了
    // 为了解决这个问题,Java就提供了一个东西:可变参数
    result = sum(a, b, c, d, 40);
    System.out.println("result:" + result);

    result = sum(a, b, c, d, 40, 50);
    System.out.println("result:" + result);
}

public static int sum(int... a) {
    int s = 0;

    for(int x : a){
        s +=x;
    }

    return s;
}

(3)Arrays工具类的一个方法
asList()把数组转成集合。

注意:这个集合的长度不能改变。

List<String> list = Arrays.asList("hello", "world", "java");
        // UnsupportedOperationException 虽然可以把数组转成集合,但是集合的长度不能改变。
//         list.add("javaee");
        // UnsupportedOperationException
        // list.remove(1);
        list.set(1, "javaee");

        for (String s : list) {
            System.out.println(s);
        }

6:练习(掌握)

A:集合的嵌套遍历

 *               需求:
 * 我们班有学生,每一个学生是不是一个对象。所以我们可以使用一个集合表示我们班级的学生。ArrayListString<Student>
 * 但是呢,我们旁边是不是还有班级,每个班级是不是也是一个ArrayList<Student>。
 * 而我现在有多个ArrayList<Student>。也要用集合存储,怎么办呢?

 * 就是这个样子的:ArrayListString<ArrayListString<Student>>

// 创建大集合
ArrayList<ArrayList<Student>> bigArrayList = new ArrayList<ArrayList<Student>>();

// 创建第一个班级的学生集合
ArrayList<Student> firstArrayList = new ArrayList<Student>();
// 创建学生
Student s1 = new Student("唐僧", 30);
Student s2 = new Student("孙悟空", 29);
Student s3 = new Student("猪八戒", 28);
Student s4 = new Student("沙僧", 27);
Student s5 = new Student("白龙马", 26);
// 学生进班
firstArrayList.add(s1);
firstArrayList.add(s2);
firstArrayList.add(s3);
firstArrayList.add(s4);
firstArrayList.add(s5);
// 把第一个班级存储到学生系统中
bigArrayList.add(firstArrayList);

// 创建第二个班级的学生集合
ArrayList<Student> secondArrayList = new ArrayList<Student>();
// 创建学生
Student s11 = new Student("诸葛亮", 30);
Student s22 = new Student("司马懿", 28);
Student s33 = new Student("周瑜", 26);
// 学生进班
secondArrayList.add(s11);
secondArrayList.add(s22);
secondArrayList.add(s33);
// 把第二个班级存储到学生系统中
bigArrayList.add(secondArrayList);

// 创建第三个班级的学生集合
ArrayList<Student> thirdArrayList = new ArrayList<Student>();
// 创建学生
Student s111 = new Student("宋江", 40);
Student s222 = new Student("吴用", 35);
Student s333 = new Student("高俅", 30);
Student s444 = new Student("李师师", 22);
// 学生进班
thirdArrayList.add(s111);
thirdArrayList.add(s222);
thirdArrayList.add(s333);
thirdArrayList.add(s444);
// 把第三个班级存储到学生系统中
bigArrayList.add(thirdArrayList);

// 遍历集合
for (ArrayList<Student> array : bigArrayList) {
    for (Student s : array) {
        System.out.println(s.getName() + "---" + s.getAge());
    }
}

B:产生10个1-20之间的随机数,要求随机数不能重复

// 创建产生随机数的对象
Random r = new Random();

// 创建一个存储随机数的集合。
ArrayList<Integer> array = new ArrayList<Integer>();

// 定义一个统计变量。从0开始。
int count = 0;

// 判断统计遍历是否小于10
while (count < 10) {
    //先产生一个随机数
    int number = r.nextInt(20) + 1;

    //判断该随机数在集合中是否存在。
    if(!array.contains(number)){
        //如果不存在:就添加,统计变量++。
        array.add(number);
        count++;
    }
}

//遍历集合
for(Integer i : array){
    System.out.println(i);
}

C:键盘录入多个数据,以0结束,并在控制台输出最大值

 *             键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
 *
 * 分析:
 * A:创建键盘录入数据对象
 * B:键盘录入多个数据,我们不知道多少个,所以用集合存储
 * C:以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
 * D:把集合转成数组
 * E:对数组排序
 * F:获取该数组中的最大索引的值

public static void main(String[] args) {
    // 创建键盘录入数据对象
    Scanner sc = new Scanner(System.in);

    // 键盘录入多个数据,我们不知道多少个,所以用集合存储
    ArrayList<Integer> array = new ArrayList<Integer>();

    // 以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
    while (true) {
        System.out.println("请输入数据:");
        int number = sc.nextInt();
        if (number != 0) {
            array.add(number);
        } else {
            break;
        }
    }

    // 把集合转成数组
    // public <T> T[] toArray(T[] a)
    Integer[] i = new Integer[array.size()];
    // Integer[] ii = array.toArray(i);
    array.toArray(i);
    // System.out.println(i);
    // System.out.println(ii);

    // 对数组排序
    // public static void sort(Object[] a)
    Arrays.sort(i);

    // 获取该数组中的最大索引的值
    System.out.println("数组是:" + arrayToString(i) + "最大值是:"
            + i[i.length - 1]);
}

public static String arrayToString(Integer[] i) {
    StringBuilder sb = new StringBuilder();

    sb.append("[");
    for (int x = 0; x < i.length; x++) {
        if (x == i.length - 1) {
            sb.append(i[x]);
        } else {
            sb.append(i[x]).append(", ");
        }
    }
    sb.append("]");

    return sb.toString();
}

 

7:要掌握的代码

集合存储元素,加入泛型,并可以使用增强for遍历。

 

============================================================================

1:登录注册案例(理解)

 

2:Set集合(理解)

(1)Set集合的特点
无序,唯一
(2)HashSet集合(掌握)
A:底层数据结构是哈希表(是一个元素为链表的数组)
B:哈希表底层依赖两个方法:hashCode()和equals()
  执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合

C:如何保证元素唯一性的呢?
由hashCode()和equals()保证的
D:开发的时候,代码非常的简单,自动生成即可。
E:HashSet存储字符串并遍历

// 创建集合对象
        HashSet<String> hs = new HashSet<String>();

        // 创建并添加元素
         hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("world");

        // 遍历集合
        for (String s : hs) {
            System.out.println(s);
        }
//        world
//        java
//        hello

F:HashSet存储自定义对象并遍历(对象的成员变量值相同即为同一个元素)

// 创建集合对象
        HashSet<Student> hs = new HashSet<Student>();

        // 创建学生对象
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("柳岩", 22);
        Student s3 = new Student("王祖贤", 30);
        Student s4 = new Student("林青霞", 27);
        Student s5 = new Student("林青霞", 20);
        Student s6 = new Student("范冰冰", 22);

        // 添加元素
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        hs.add(s5);
        hs.add(s6);

        // 遍历集合
        for (Student s : hs) {
            System.out.println(s.getName() + "---" + s.getAge());
        }

//        林青霞---20
//        范冰冰---22
//        柳岩---22
//        王祖贤---30
//        林青霞---27

        (3)LinkedHashSet集合

 * LinkedHashSet:底层数据结构由哈希表链表组成。
 * 哈希表保证元素的唯一性。
 * 链表保证元素有序。(存储和取出是一致)

// 创建集合对象
        LinkedHashSet<String> hs = new LinkedHashSet<String>();

        // 创建并添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("world");
        hs.add("java");

        // 遍历
        for (String s : hs) {
            System.out.println(s);
        }

//        hello
//        world
//        java

        (4)TreeSet集合
A:底层数据结构是红黑树(是一个自平衡的二叉树)
B:保证元素的排序方式
a:自然排序(元素具备比较性)
让元素所属的类实现Comparable接口 public class Student1 implements Comparable<Student1> {

年龄排序:

@Override
public int compareTo(Student1 s) {
    // 这里返回什么,其实应该根据我的排序规则来做
    // 按照年龄排序,主要条件
    int num = this.age - s.age;
    // 次要条件
    // 年龄相同的时候,还得去看姓名是否也相同
    // 如果年龄和姓名都相同,才是同一个元素
    int num2 = num == 0 ? this.name.compareTo(s.name) : num;
    return num2;
}
        // 创建集合对象
        TreeSet<Student1> ts = new TreeSet<Student1>();

        // 创建元素
        Student1 s1 = new Student1("linqingxia", 27);
        Student1 s2 = new Student1("zhangguorong", 29);
        Student1 s3 = new Student1("wanglihong", 23);
        Student1 s4 = new Student1("linqingxia", 27);
        Student1 s5 = new Student1("liushishi", 22);
        Student1 s6 = new Student1("wuqilong", 40);
        Student1 s7 = new Student1("fengqingy", 22);

        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);

        // 遍历
        for (Student1 s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }

//        fengqingy---22
//        liushishi---22
//        wanglihong---23
//        linqingxia---27
//        zhangguorong---29
//        wuqilong---40

姓名长度排序:

@Override
public int compareTo(Student2 s) {
    // 主要条件 姓名的长度
    int num = this.name.length() - s.name.length();
    // 姓名的长度相同,不代表姓名的内容相同
    int num2 = num == 0 ? this.name.compareTo(s.name) : num;
    // 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
    int num3 = num2 == 0 ? this.age - s.age : num2;
    return num3;
}

 

// 创建集合对象
        TreeSet<Student2> ts = new TreeSet<Student2>();

        // 创建元素
        Student2 s1 = new Student2("linqingxia", 27);
        Student2 s2 = new Student2("zhangguorong", 29);
        Student2 s3 = new Student2("wanglihong", 23);
        Student2 s4 = new Student2("linqingxia", 27);
        Student2 s5 = new Student2("liushishi", 22);
        Student2 s6 = new Student2("wuqilong", 40);
        Student2 s7 = new Student2("fengqingy", 22);
        Student2 s8 = new Student2("linqingxia", 29);

        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        ts.add(s8);

        // 遍历
        for (Student2 s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
//        wuqilong---40
//        fengqingy---22
//        liushishi---22
//        linqingxia---27
//        linqingxia---29
//        wanglihong---23
//        zhangguorong---29

b:比较器排序(集合具备比较性)
让集合构造方法接收Comparator的实现类对象

public class Student3 {
    private String name;
    private int age;

    public Student3() {
        super();
    }

    public Student3(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;
    }
}
public class TreeSetMyComparator implements Comparator<Student3> {

    @Override
    public int compare(Student3 s1, Student3 s2) {
        // 姓名长度
        int num = s1.getName().length() - s2.getName().length();
        // 姓名内容
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
        // 年龄
        int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
        return num3;
    }
}

 

        // 创建集合对象
        // TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
        // public TreeSet(Comparator comparator) //比较器排序
        // TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());

        // 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
        // 而匿名内部类就可以实现这个东西
        TreeSet<Student3> ts = new TreeSet<Student3>(new Comparator<Student3>() {
            @Override
            public int compare(Student3 s1, Student3 s2) {
                // 姓名长度
                int num = s1.getName().length() - s2.getName().length();
                // 姓名内容
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                // 年龄
                int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                return num3;
            }
        });

        // 创建元素
        Student3 s1 = new Student3("linqingxia", 27);
        Student3 s2 = new Student3("zhangguorong", 29);
        Student3 s3 = new Student3("wanglihong", 23);
        Student3 s4 = new Student3("linqingxia", 27);
        Student3 s5 = new Student3("liushishi", 22);
        Student3 s6 = new Student3("wuqilong", 40);
        Student3 s7 = new Student3("fengqingy", 22);
        Student3 s8 = new Student3("linqingxia", 29);

        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        ts.add(s8);

        // 遍历
        for (Student3 s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }

//        wuqilong---40
//        fengqingy---22
//        liushishi---22
//        linqingxia---27
//        linqingxia---29
//        wanglihong---23
//        zhangguorong---29


C:把我们讲过的代码看一遍即可

(4)案例:
A:获取无重复的随机数

/*
 * 编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
 *
 * 分析:
 *        A:创建随机数对象
 *        B:创建一个HashSet集合
 *        C:判断集合的长度是不是小于10
 *           是:就创建一个随机数添加
 *           否:不搭理它
 *        D:遍历HashSet集合
 */
public class HashSetTest {
    public static void main(String[] args) {
        // 创建随机数对象
        Random r = new Random();

        // 创建一个Set集合
        HashSet<Integer> ts = new HashSet<Integer>();

        // 判断集合的长度是不是小于10
        while (ts.size() < 10) {
            int num = r.nextInt(20) + 1;
            ts.add(num);
        }

        // 遍历Set集合
        for (Integer i : ts) {
            System.out.println(i);
        }
    }
}


B:键盘录入学生按照总分从高到底输出

/*
 * 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
 *
 * 分析:
 *        A:定义学生类
 *        B:创建一个TreeSet集合
 *        C:总分从高到底如何实现呢?
 *        D:键盘录入5个学生信息
 *        E:遍历TreeSet集合
 */
public class TreeSetTest {
    public static void main(String[] args) {
        // 创建一个TreeSet集合
        TreeSet<Student4> ts = new TreeSet<Student4>(new Comparator<Student4>() {
            @Override
            public int compare(Student4 s1, Student4 s2) {
                // 总分从高到低
                int num = s2.getSum() - s1.getSum();
                // 总分相同的不一定语文相同
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                // 总分相同的不一定数序相同
                int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
                // 总分相同的不一定英语相同
                int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
                // 姓名还不一定相同呢
                int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
                        : num4;
                return num5;
            }
        });

        System.out.println("学生信息录入开始");
        // 键盘录入5个学生信息
        for (int x = 1; x <= 5; x++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入第" + x + "个学生的姓名:");
            String name = sc.nextLine();
            System.out.println("请输入第" + x + "个学生的语文成绩:");
            String chineseString = sc.nextLine();
            System.out.println("请输入第" + x + "个学生的数学成绩:");
            String mathString = sc.nextLine();
            System.out.println("请输入第" + x + "个学生的英语成绩:");
            String englishString = sc.nextLine();

            // 把数据封装到学生对象中
            Student4 s = new Student4();
            s.setName(name);
            s.setChinese(Integer.parseInt(chineseString));
            s.setMath(Integer.parseInt(mathString));
            s.setEnglish(Integer.parseInt(englishString));

            // 把学生对象添加到集合
            ts.add(s);
        }
        System.out.println("学生信息录入完毕");

        System.out.println("学习信息从高到低排序如下:");
        System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
        // 遍历集合
        for (Student4 s : ts) {
            System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
                    + s.getMath() + "\t" + s.getEnglish());
        }
    }
}

 

3:Collection集合总结(掌握)

 

Collection
|--List 有序,可重复
|--ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高
|--Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低
|--LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高
|--Set 无序,唯一
|--HashSet
底层数据结构是哈希表。
如何保证元素唯一性的呢?
依赖两个方法:hashCode()和equals()
开发中自动生成这两个方法即可
|--LinkedHashSet
底层数据结构是链表和哈希表
由链表保证元素有序
由哈希表保证元素唯一
|--TreeSet
底层数据结构是红黑树。
如何保证元素排序的呢?
自然排序
比较器排序
如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定

4:针对Collection集合我们到底使用谁呢?(掌握)

唯一吗?
是:Set
排序吗?
是:TreeSet
否:HashSet
如果你知道是Set,但是不知道是哪个Set,就用HashSet。

否:List
要安全吗?
是:Vector
否:ArrayList或者LinkedList
查询多:ArrayList
增删多:LinkedList
如果你知道是List,但是不知道是哪个List,就用ArrayList。

如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。

如果你知道用集合,就用ArrayList。

5:在集合中常见的数据结构(掌握)

ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

============================================================================

 

1:Map(掌握)

(1)将键映射到值的对象。一个映射不能包含重复的键每个键最多只能映射到一个值。 

 *             注意:
 * Map集合的数据结构值针对键有效,跟值无关
 * HashMap,TreeMap等会讲。
 * Collection集合的数据结构是针对元素有效

(2)Map和Collection的区别?
A:Map 存储的是键值对形式的元素,键唯一,值可以重复。夫妻对
B:Collection 存储的是单独出现的元素,子接口Set元素唯一,子接口List元素可重复。光棍
(3)Map接口功能概述(自己补齐)
 * 1:添加功能
 * V put(K key,V value):添加元素。这个其实还有另一个功能?先不告诉你,等会讲
 * 如果键是第一次存储,就直接存储元素,返回null
 * 如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值

 * 2:删除功能
 * void clear():移除所有的键值对元素
 * V remove(Object key):根据键删除键值对元素,并把值返回
 * 3:判断功能
 * boolean containsKey(Object key):判断集合是否包含指定的键
 * boolean containsValue(Object value):判断集合是否包含指定的值
 * boolean isEmpty():判断集合是否为空
 * 4:获取功能
 * Set<Map.Entry<K,V>> entrySet():???
 * V get(Object key):根据键获取值
 * Set<K> keySet():获取集合中所有键的集合
 * Collection<V> values():获取集合中所有值的集合
 * 5:长度功能

 * int size():返回集合中的键值对的对数

// 创建集合对象
Map<String, String> map = new HashMap<String, String>();

/**
 * 添加元素。
 * V put(K key,V value)
 * 这个其实还有另一个功能?先不告诉你,等会讲
 */
System.out.println("put:" + map.put("文章", "马伊俐")); // null 如果键是第一次存储,就直接存储元素,返回null
System.out.println("put:" + map.put("文章", "姚笛")); // 马伊俐 如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值

map.put("邓超", "孙俪");
map.put("黄晓明", "杨颖");
map.put("周杰伦", "蔡依林");
map.put("刘恺威", "杨幂");

/**
 * 删除功能
 * void clear():移除所有的键值对元素
 */
//map.clear();

/**
 * V remove(Object key)
 * 根据键删除键值对元素,并把值返回
 */
//System.out.println("remove:" + map.remove("黄晓明"));
//System.out.println("remove:" + map.remove("黄晓波"));

/**
 * boolean containsKey(Object key)
 * 判断集合是否包含指定的键
 */
 System.out.println("containsKey:" + map.containsKey("黄晓明"));
 System.out.println("containsKey:" + map.containsKey("黄晓波"));

/**
 * boolean containsValue(Object value):判断集合是否包含指定的值
 */
System.out.println("containsValue:" + map.containsValue("杨1幂"));

/**
 * boolean isEmpty()
 * 判断集合是否为空
 */
 System.out.println("isEmpty:"+map.isEmpty());

/**
 * int size()
 * 返回集合中的键值对的对数
 */
System.out.println("size:"+map.size());

// 输出集合名称
System.out.println("map:" + map);
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();

// 创建元素并添加元素
map.put("邓超", "孙俪");
map.put("黄晓明", "杨颖");
map.put("周杰伦", "蔡依林");
map.put("刘恺威", "杨幂");

/**
 * V get(Object key)
 * 根据键获取值
 */
System.out.println("get:" + map.get("周杰伦"));
System.out.println("get:" + map.get("周杰")); // 返回null
System.out.println("----------------------");

/**
 * Set<K> keySet()
 * 获取集合中所有键的集合
 */
Set<String> set = map.keySet();
for (String key : set) {
    System.out.println(key);
}
System.out.println("----------------------");

/**
 * Collection<V> values()
 * 获取集合中所有值的集合
 */
Collection<String> con = map.values();
for (String value : con) {
    System.out.println(value);
}

(4)Map集合的遍历
A:键找值
a:获取所有键的集合
b:遍历键的集合,得到每一个键
c:根据键到集合中去找值

// 创建集合对象
Map<String, String> map = new HashMap<String, String>();

// 创建元素并添加到集合
map.put("杨过", "小龙女");
map.put("郭靖", "黄蓉");
map.put("杨康", "穆念慈");
map.put("陈玄风", "梅超风");

// 遍历
// 获取所有的键
Set<String> set = map.keySet();
// 遍历键的集合,获取得到每一个键
for (String key : set) {
    // 根据键去找值
    String value = map.get(key);
    System.out.println(key + "---" + value);
}


B:键值对对象找键和值
a:获取所有的键值对对象的集合
b:遍历键值对对象的集合,获取每一个键值对对象
c:根据键值对对象去获取键和值

// 创建集合对象
Map<String, String> map = new HashMap<String, String>();

// 创建元素并添加到集合
map.put("杨过", "小龙女");
map.put("郭靖", "黄蓉");
map.put("杨康", "穆念慈");
map.put("陈玄风", "梅超风");

System.out.println(map); // {陈玄风=梅超风, 杨过=小龙女, 杨康=穆念慈, 郭靖=黄蓉}
// 获取所有键值对对象的集合
Set<Map.Entry<String, String>> set = map.entrySet();

System.out.println(set); // [陈玄风=梅超风, 杨过=小龙女, 杨康=穆念慈, 郭靖=黄蓉]
// 遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : set) {
    // 根据键值对对象获取键和值
    String key = me.getKey();
    String value = me.getValue();
    System.out.println(key + "---" + value);
}


代码体现:
Map<String,String> hm = new HashMap<String,String>();

hm.put("it002","hello");
hm.put("it003","world");
hm.put("it001","java");

//方式1 键找值
Set<String> set = hm.keySet();
for(String key : set) {
String value = hm.get(key);
System.out.println(key+"---"+value);
}

//方式2 键值对对象找键和值
Set<Map.Entry<String,String>> set2 = hm.entrySet();
for(Map.Entry<String,String> me : set2) {
String key = me.getKey();
String value = me.getValue();
System.out.println(key+"---"+value);
}
(5)HashMap集合的练习

A:HashMap<String,String>

// 创建集合对象
HashMap<String, String> hm = new HashMap<String, String>();

hm.put("it001", "马云");
hm.put("it003", "马化腾");
hm.put("it004", "乔布斯");
hm.put("it005", "张朝阳");
hm.put("it002", "裘伯君"); // wps
hm.put("it001", "比尔盖茨");

// 遍历
Set<String> set = hm.keySet();
for (String key : set) {
    String value = hm.get(key);
    System.out.println(key + "---" + value);
}

B:HashMap<Integer,String>

// 创建集合对象
HashMap<Integer, String> hm = new HashMap<Integer, String>();

hm.put(27, "林青霞");
hm.put(30, "风清扬");
hm.put(28, "刘意");
hm.put(29, "林青霞");

// 下面的写法是八进制,但是不能出现8以上的单个数据
// hm.put(003, "hello");
// hm.put(006, "hello");
// hm.put(007, "hello");
// hm.put(008, "hello");

// 遍历
Set<Integer> set = hm.keySet();
for (Integer key : set) {
    String value = hm.get(key);
    System.out.println(key + "---" + value);
}

// 下面这种方式仅仅是集合的元素的字符串表示
System.out.println("hm:" + hm); // {27=林青霞, 28=刘意, 29=林青霞, 30=风清扬}

C:HashMap<String,Student>

// 创建集合对象
HashMap<String, Student> hm = new HashMap<String, Student>();

// 创建学生对象
Student s1 = new Student("周星驰", 58);
Student s2 = new Student("刘德华", 55);
Student s3 = new Student("梁朝伟", 54);
Student s4 = new Student("刘嘉玲", 50);

// 添加元素
hm.put("9527", s1);
hm.put("9522", s2);
hm.put("9524", s3);
hm.put("9529", s4);

// 遍历
Set<String> set = hm.keySet();
for (String key : set) {
    // 注意了:这次值不是字符串了
    // String value = hm.get(key);
    Student value = hm.get(key);
    System.out.println(key + "---" + value.getName() + "---"
            + value.getAge());
}

D:HashMap<Student,String>

// 创建集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();

// 创建学生对象
Student s1 = new Student("貂蝉", 27);
Student s2 = new Student("王昭君", 30);
Student s3 = new Student("西施", 33);
Student s4 = new Student("杨玉环", 35);
Student s5 = new Student("貂蝉", 27);

// 添加元素
hm.put(s1, "8888");
hm.put(s2, "6666");
hm.put(s3, "5555");
hm.put(s4, "7777");
hm.put(s5, "9999");

// 遍历
Set<Student> set = hm.keySet();
for (Student key : set) {
    String value = hm.get(key);
    System.out.println(key.getName() + "---" + key.getAge() + "---"
            + value);
}

        (6) LinkedHashMap:

                是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。

 * 由哈希表保证键的唯一性

 * 由链表保证键盘的有序(存储和取出的顺序一致)

// 创建集合对象
        LinkedHashMap<String, String> hm = new LinkedHashMap<String, String>();

        // 创建并添加元素
        hm.put("2345", "hello");
        hm.put("1234", "world");
        hm.put("3456", "java");
        hm.put("1234", "javaee"); // 代替
        hm.put("3456", "android");

        // 遍历
        Set<String> set = hm.keySet();
        System.out.println(set);
        for (String key : set) {
            String value = hm.get(key);
            System.out.println(key + "---" + value);
        }
//        2345---hello
//        1234---javaee
//        3456---android


(7TreeMap集合的练习

A:TreeMap<String,String>

// 创建集合对象
        TreeMap<String, String> tm = new TreeMap<String, String>();

        // 创建元素并添加元素
        tm.put("hello", "你好");
        tm.put("world", "世界");
        tm.put("java", "爪哇");
        tm.put("world", "世界2");
        tm.put("javaee", "爪哇EE");

        // 遍历集合
        Set<String> set = tm.keySet();
        for (String key : set) {
            String value = tm.get(key);
            System.out.println(key + "---" + value);
        }
        // 默认按键的升序排序 
//        hello---你好
//        java---爪哇
//        javaee---爪哇EE
//        world---世界2

 

B:TreeMap<Student,String>

// 创建集合对象
        TreeMap<Student1, String> tm = new TreeMap<Student1, String>(
                new Comparator<Student1>() {
                    @Override
                    public int compare(Student1 s1, Student1 s2) {
                        // 主要条件
                        int num = s1.getAge() - s2.getAge();
                        // 次要条件
                        int num2 = num == 0 ? s1.getName().compareTo(
                                s2.getName()) : num;
                        return num2;
                    }
                });

        // 创建学生对象
        Student1 s1 = new Student1("潘安", 30);
        Student1 s2 = new Student1("柳下惠", 35);
        Student1 s3 = new Student1("唐伯虎", 33);
        Student1 s4 = new Student1("燕青", 32);
        Student1 s5 = new Student1("唐伯虎", 33);

        // 存储元素
        tm.put(s1, "宋朝");
        tm.put(s2, "元朝");
        tm.put(s3, "明朝");
        tm.put(s4, "清朝");
        tm.put(s5, "汉朝");

        // 遍历
        Set<Student1> set = tm.keySet();
        for (Student1 key : set) {
            String value = tm.get(key);
            System.out.println(key.getName() + "---" + key.getAge() + "---"
                    + value);
        }
//        潘安---30---宋朝
//        燕青---32---清朝
//        唐伯虎---33---汉朝
//        柳下惠---35---元朝


(8)案例

A:统计一个字符串中每个字符出现的次数

/*
 * 需求 :"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
 *
 * 分析:
 *        A:定义一个字符串(可以改进为键盘录入)
 *        B:定义一个TreeMap集合
 *           键:Character
 *           值:Integer
 *        C:把字符串转换为字符数组
 *        D:遍历字符数组,得到每一个字符
 *        E:拿刚才得到的字符作为键到集合中去找值,看返回值
 *           是null:说明该键不存在,就把该字符作为键,1作为值存储
 *           不是null:说明该键存在,就把值加1,然后重写存储该键和值
 *        F:定义字符串缓冲区变量
 *        G:遍历集合,得到键和值,进行按照要求拼接
 *        H:把字符串缓冲区转换为字符串输出
 *
 * 录入:linqingxia
 * 结果:result:a(1)g(1)i(3)l(1)n(2)q(1)x(1)
 */
public class TreeMap3TestCharCount {
    public static void main(String[] args) {
        // 定义一个字符串(可以改进为键盘录入)
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String line = sc.nextLine();

        // 定义一个TreeMap集合
        TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();

        //把字符串转换为字符数组
        char[] chs = line.toCharArray();

        //遍历字符数组,得到每一个字符
        for(char ch : chs){
            //拿刚才得到的字符作为键到集合中去找值,看返回值
            Integer i =  tm.get(ch);

            //是null:说明该键不存在,就把该字符作为键,1作为值存储
            if(i == null){
                tm.put(ch, 1);
            }else {
                //不是null:说明该键存在,就把值加1,然后重写存储该键和值
                i++;
                tm.put(ch,i);
            }
        }

        //定义字符串缓冲区变量
        StringBuilder sb=  new StringBuilder();

        //遍历集合,得到键和值,进行按照要求拼接
        Set<Character> set = tm.keySet();
        for(Character key : set){
            Integer value = tm.get(key);
            sb.append(key).append("(").append(value).append(")");
        }

        //把字符串缓冲区转换为字符串输出
        String result = sb.toString();
        System.out.println("result:"+result);
//        aababcabcdabcde
//        result:a(5)b(4)c(3)d(2)e(1)
    }
}

B:集合的嵌套遍历

a:HashMap嵌套HashMap

/*
 * HashMap嵌套HashMap
 *
 *        a班
 *                小红  20
 *                小绿  22
 *
 *        b班
 *                小蓝   21
 *             小紫  23
 * 先存储元素,然后遍历元素
 */
public class NestHashMapInHashMap {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, HashMap<String, Integer>> czbkMap = new HashMap<String, HashMap<String, Integer>>();

        // 创建a班集合对象
        HashMap<String, Integer> aMap = new HashMap<String, Integer>();
        // 添加元素
        aMap.put("小红", 20);
        aMap.put("小绿", 22);
        // 把a班添加到大集合
        czbkMap.put("a", aMap);

        // 创建b班集合对象
        HashMap<String, Integer> bMap = new HashMap<String, Integer>();
        // 添加元素
        bMap.put("小蓝", 21);
        bMap.put("小紫", 23);
        // 把b班添加到大集合
        czbkMap.put("b", bMap);

        //遍历集合
        System.out.println(czbkMap); // {a={小红=20, 小绿=22}, b={小蓝=21, 小紫=23}}
        Set<String> czbkMapSet = czbkMap.keySet();
        System.out.println(czbkMapSet); // [a, b]

        for(String czbkMapKey : czbkMapSet){
            System.out.println(czbkMapKey);
            HashMap<String, Integer> czbkMapValue = czbkMap.get(czbkMapKey);
            Set<String> czbkMapValueSet = czbkMapValue.keySet();
            for(String czbkMapValueKey : czbkMapValueSet){
                Integer czbkMapValueValue = czbkMapValue.get(czbkMapValueKey);
                System.out.println("\t"+czbkMapValueKey+"---"+czbkMapValueValue);
            }
        }
    }
}

b:HashMap嵌套ArrayList

/*
 *需求:
 *假设HashMap集合的元素是ArrayList。有3个。
 *每一个ArrayList集合的值是字符串。
 *元素我已经完成,请遍历。
 *结果:
 *     三国演义
 *       吕布
 *       周瑜
 *     笑傲江湖
 *       令狐冲
 *       林平之
 *     神雕侠侣
 *       郭靖
 *       杨过
 */
public class NestHashMapInArrayList {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();

        // 创建元素集合1
        ArrayList<String> array1 = new ArrayList<String>();
        array1.add("吕布");
        array1.add("周瑜");
        hm.put("三国演义", array1);

        // 创建元素集合2
        ArrayList<String> array2 = new ArrayList<String>();
        array2.add("令狐冲");
        array2.add("林平之");
        hm.put("笑傲江湖", array2);

        // 创建元素集合3
        ArrayList<String> array3 = new ArrayList<String>();
        array3.add("郭靖");
        array3.add("杨过");
        hm.put("神雕侠侣", array3);

        //遍历集合
        Set<String> set = hm.keySet();
        for(String key : set){
            System.out.println(key);
            ArrayList<String> value = hm.get(key);
            for(String s : value){
                System.out.println("\t"+s);
            }
        }
    }
}

c:ArrayList嵌套HashMap

/*
 ArrayList集合嵌套HashMap集合并遍历。
 需求:
 假设ArrayList集合的元素是HashMap。有3个。
 每一个HashMap集合的键和值都是字符串。
 元素我已经完成,请遍历。
 结果:
 周瑜---小乔
 吕布---貂蝉

 郭靖---黄蓉
 杨过---小龙女

 令狐冲---任盈盈
 林平之---岳灵珊
 */
public class NestHashMapInArrayList {
    public static void main(String[] args) {
        // 创建集合对象
        ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();

        // 创建元素1
        HashMap<String, String> hm1 = new HashMap<String, String>();
        hm1.put("周瑜", "小乔");
        hm1.put("吕布", "貂蝉");
        // 把元素添加到array里面
        array.add(hm1);

        // 创建元素1
        HashMap<String, String> hm2 = new HashMap<String, String>();
        hm2.put("郭靖", "黄蓉");
        hm2.put("杨过", "小龙女");
        // 把元素添加到array里面
        array.add(hm2);

        // 创建元素1
        HashMap<String, String> hm3 = new HashMap<String, String>();
        hm3.put("令狐冲", "任盈盈");
        hm3.put("林平之", "岳灵珊");
        // 把元素添加到array里面
        array.add(hm3);

        // 遍历
        for (HashMap<String, String> hm : array) {
            Set<String> set = hm.keySet();
            for (String key : set) {
                String value = hm.get(key);
                System.out.println(key + "---" + value);
            }
        }
    }
}

d:多层嵌套

// 创建大集合
HashMap<String, HashMap<String, ArrayList<Student>>> czbkMap = new HashMap<String, HashMap<String, ArrayList<Student>>>();

// 北京数据
HashMap<String, ArrayList<Student>> bjCzbkMap = new HashMap<String, ArrayList<Student>>();
ArrayList<Student> array1 = new ArrayList<Student>();
Student s1 = new Student("林xx", 27);
Student s2 = new Student("风xx", 30);
array1.add(s1);
array1.add(s2);
ArrayList<Student> array2 = new ArrayList<Student>();
Student s3 = new Student("赵xx", 28);
Student s4 = new Student("武xx", 29);
array2.add(s3);
array2.add(s4);
bjCzbkMap.put("A班", array1);
bjCzbkMap.put("B班", array2);
czbkMap.put("BJ", bjCzbkMap);

// 西安校区数据
HashMap<String, ArrayList<Student>> xaCzbkMap = new HashMap<String, ArrayList<Student>>();
ArrayList<Student> array3 = new ArrayList<Student>();
Student s5 = new Student("范xx", 27);
Student s6 = new Student("刘xx", 30);
array3.add(s5);
array3.add(s6);
ArrayList<Student> array4 = new ArrayList<Student>();
Student s7 = new Student("李xx", 28);
Student s8 = new Student("张xx", 29);
array4.add(s7);
array4.add(s8);
xaCzbkMap.put("A班", array3);
xaCzbkMap.put("B班", array4);
czbkMap.put("XA", xaCzbkMap);

// 遍历集合
Set<String> czbkMapSet = czbkMap.keySet();
for (String czbkMapKey : czbkMapSet) {
    System.out.println(czbkMapKey);
    HashMap<String, ArrayList<Student>> czbkMapValue = czbkMap
            .get(czbkMapKey);
    Set<String> czbkMapValueSet = czbkMapValue.keySet();
    for (String czbkMapValueKey : czbkMapValueSet) {
        System.out.println("\t" + czbkMapValueKey);
        ArrayList<Student> czbkMapValueValue = czbkMapValue
                .get(czbkMapValueKey);
        for (Student s : czbkMapValueValue) {
            System.out.println("\t\t" + s.getName() + "---"
                    + s.getAge());
        }
    }
}

 

2:Collections(理解)

(1)是针对集合进行操作的工具类
(2)面试题:Collection和Collections的区别
A:Collection 是单列集合的顶层接口,有两个子接口List和Set
B:Collections 是针对集合进行操作的工具类,可以对集合进行排序和查找等

(3)常见的几个小方法:
 * public static <T> void sort(List<T> list)                            排序 默认情况下是自然顺序。
 * public static <T> int binarySearch(List<?> list,T key)       二分查找
 * public static <T> T max(Collection<?> coll)                     最大值
 * public static void reverse(List<?> list)                               反转

* public static void shuffle(List<?> list)                                随机置换

// 创建集合对象
List<Integer> list = new ArrayList<Integer>();

// 添加元素
list.add(30);
list.add(20);
list.add(50);
list.add(10);
list.add(40);

System.out.println("list:" + list); // [30, 20, 50, 10, 40]

/**
 * public static <T> void sort(List<T> list)
 * 排序 默认情况下是自然顺序。
 */
 Collections.sort(list);
 System.out.println("list:" + list); // [10, 20, 30, 40, 50]

/**
 * public static <T> int binarySearch(List<?> list,T key)
 * 二分查找
 */
 System.out.println("binarySearch:" + Collections.binarySearch(list, 30)); // 2
 System.out.println("binarySearch:" + Collections.binarySearch(list, 300)); // -6

/**
 * public static <T> T max(Collection<?> coll)
 * 最大值
 */
 System.out.println("max:"+Collections.max(list)); // 50

/**
 * public static void reverse(List<?> list)
 * 反转
 */
 Collections.reverse(list);
 System.out.println("list:" + list); // [50, 40, 30, 20, 10]

/**
 * public static void shuffle(List<?> list)
 * 随机置换
 */
Collections.shuffle(list);
System.out.println("list:" + list); // [50, 40, 30, 20, 10]

(4)案例 

A:ArrayList集合存储自定义对象的排序

// 创建集合对象
        List<Student> list = new ArrayList<Student>();

        // 创建学生对象
         Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("风清扬", 30);
        Student s3 = new Student("刘晓曲", 28);
        Student s4 = new Student("武鑫", 29);
        Student s5 = new Student("林青霞", 27);

        // 添加元素对象
         list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);
        list.add(s5);

        // 排序
         // 自然排序
         // Collections.sort(list);
        // 比较器排序
         // 如果同时有自然排序和比较器排序,以比较器排序为主
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s2.getAge() - s1.getAge();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
                        : num;
                return num2;
            }
        });

        // 遍历集合
        for (Student s : list) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
//        风清扬---30
//        武鑫---29
//        刘晓曲---28
//        林青霞---27
//        林青霞---27

B:模拟斗地主洗牌和发牌

/*
 * 模拟斗地主洗牌和发牌
 *
 * 分析:
 *        A:创建一个牌盒
 *        B:装牌
 *        C:洗牌
 *        D:发牌
 *        E:看牌
 */
public class Collections3PokerGameTest {
    public static void main(String[] args) {
        // 创建一个牌盒
        ArrayList<String> array = new ArrayList<String>();

        // 装牌
        // 黑桃A,黑桃2,黑桃3,...黑桃K
        // 红桃A,...
        // 梅花A,...
        // 方块A,...
        // 定义一个花色数组
        String[] colors = { "♠", "♥", "♣", "♦" };
        // 定义一个点数数组
        String[] numbers = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                "J", "Q", "K" };
        // 装牌
        for (String color : colors) {
            for (String number : numbers) {
                array.add(color.concat(number));
            }
        }
        array.add("小王");
        array.add("大王");

        // 洗牌
        Collections.shuffle(array);

        // System.out.println("array:" + array);

        // 发牌
        ArrayList<String> fengQingYang = new ArrayList<String>();
        ArrayList<String> linQingXia = new ArrayList<String>();
        ArrayList<String> liuYi = new ArrayList<String>();
        ArrayList<String> diPai = new ArrayList<String>();

        for (int x = 0; x < array.size(); x++) {
            if (x >= array.size() - 3) {
                diPai.add(array.get(x));
            } else if (x % 3 == 0) {
                fengQingYang.add(array.get(x));
            } else if (x % 3 == 1) {
                linQingXia.add(array.get(x));
            } else if (x % 3 == 2) {
                liuYi.add(array.get(x));
            }
        }

        // 看牌
        lookPoker("风清扬", fengQingYang);
        lookPoker("林青霞", linQingXia);
        lookPoker("刘意", liuYi);

        lookPoker("底牌", diPai);
    }

    public static void lookPoker(String name, ArrayList<String> array) {
        System.out.print(name + "的牌是:");
        for (String s : array) {
            System.out.print(s + " ");
        }
        System.out.println();
    }
}

C:模拟斗地主洗牌和发牌并对牌进行排序

/*
 * 思路:
 *        A:创建一个HashMap集合
 *        B:创建一个ArrayList集合
 *        C:创建花色数组和点数数组
 *        D:从0开始往HashMap里面存储编号,并存储对应的牌
 *        同时往ArrayList里面存储编号即可。
 *      E:洗牌(洗的是编号)
 *      F:发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
 *      G:看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
 */
public class Collections3PokerGameTest1 {
    public static void main(String[] args) {
        // 创建一个HashMap集合
        HashMap<Integer, String> hm = new HashMap<Integer, String>();

        // 创建一个ArrayList集合
        ArrayList<Integer> array = new ArrayList<Integer>();

        // 创建花色数组和点数数组
        // 定义一个花色数组
        String[] colors = { "♠", "♥", "♣", "♦" };
        // 定义一个点数数组
        String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q",
                "K", "A", "2", };

        // 从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里面存储编号即可。
        int index = 0;

        for (String number : numbers) {
            for (String color : colors) {
                String poker = color.concat(number);
                hm.put(index, poker);
                array.add(index);
                index++;
            }
        }
        hm.put(index, "小王");
        array.add(index);
        index++;
        hm.put(index, "大王");
        array.add(index);

        // 洗牌(洗的是编号)
        Collections.shuffle(array);

        // 发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
        TreeSet<Integer> fengQingYang = new TreeSet<Integer>();
        TreeSet<Integer> linQingXia = new TreeSet<Integer>();
        TreeSet<Integer> liuYi = new TreeSet<Integer>();
        TreeSet<Integer> diPai = new TreeSet<Integer>();

        for (int x = 0; x < array.size(); x++) {
            if (x >= array.size() - 3) {
                diPai.add(array.get(x));
            } else if (x % 3 == 0) {
                fengQingYang.add(array.get(x));
            } else if (x % 3 == 1) {
                linQingXia.add(array.get(x));
            } else if (x % 3 == 2) {
                liuYi.add(array.get(x));
            }
        }

        // 看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
        lookPoker("风清扬", fengQingYang, hm);
        lookPoker("林青霞", linQingXia, hm);
        lookPoker("刘意", liuYi, hm);
        lookPoker("底牌", diPai, hm);
    }

    // 写看牌的功能
    public static void lookPoker(String name, TreeSet<Integer> ts,
                                 HashMap<Integer, String> hm) {
        System.out.print(name + "的牌是:");
        for (Integer key : ts) {
            String value = hm.get(key);
            System.out.print(value + " ");
        }
        System.out.println();
    }
}

============================================================================

1:异常(理解)

(1)程序出现的不正常的情况。
(2)异常的体系
Throwable
|--Error 严重问题,我们不处理。
|--Exception
|--RuntimeException 运行期异常,我们需要修正代码
|--非RuntimeException    编译期异常,必须处理的,否则程序编译不通过
(3)异常的处理:
A:JVM的默认处理
把异常的名称,原因,位置等信息输出在控制台,但是呢程序不能继续执行了。
B:自己处理
a:try...catch...finally
自己编写处理代码,后面的程序可以继续执行
b:throws
把自己处理不了的,在方法上声明,告诉调用者,这里有问题
(4)面试题
A:编译期异常和运行期异常的区别?
编译期异常 必须要处理的,否则编译不通过
运行期异常 可以不处理,也可以处理
B:throw和throws是的区别
throw:
在方法体中,后面跟的是异常对象名,并且只能是一个
throw抛出的是一个异常对象,说明这里肯定有一个异常产生了
throws:
在方法声明上,后面跟的是异常的类名,可以是多个
throws是声明方法有异常,是一种可能性,这个异常并不一定会产生
(5)finally关键字及其面试题
A:finally用于释放资源,它的代码永远会执行。特殊情况:在执行到finally之前jvm退出了
B:面试题
a:final,finally,finalize的区别?
b:如果在catch里面有return,请问finally还执行吗?如果执行,在return前还是后
会,前。

实际上在中间。这个上课我们讲过
C:异常处理的变形
try...catch...finally
try...catch...
try...catch...catch...
try...catch...catch...fianlly
try...finally
(6)自定义异常
继承自Exception或者RuntimeException,只需要提供无参构造和一个带参构造即可
(7)异常的注意实现
A:父的方法有异常抛出,子的重写方法在抛出异常的时候必须要小于等于父的异常 
B:父的方法没有异常抛出,子的重写方法不能有异常抛出
C:父的方法抛出多个异常,子的重写方法必须比父少或者小

 

2:File(掌握)

(1)IO流操作中大部分都是对文件的操作,所以Java就提供了File类供我们来操作文件

                File:文件和目录(文件夹)路径名的抽象表示形式

(2)构造方法

 * File(String pathname):根据一个路径得到File对象
 * File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
 * File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象

/**
 * File(String pathname)
 * 根据一个路径得到File对象
 */
// 把e:\\demo\\a.txt封装成一个File对象
File file = new File("D:\\demo\\a.txt");
System.out.println(file); // D:\demo\a.txt

/**
 * File(String parent, String child)
 * 根据一个目录和一个子文件/目录得到File对象
 */
File file2 = new File("D:\\demo", "a.txt");
System.out.println(file2); // D:\demo\a.txt

/**
 * File(File parent, String child)
 * 根据一个父File对象和一个子文件/目录得到File对象
 */
File file3 = new File("d:\\demo");
System.out.println(file3); // d:\demo

File file4 = new File(file3, "a.txt");
System.out.println(file4); // d:\demo\a.txt

// 以上三种方式其实效果一样

A:File file = new File("e:\\demo\\a.txt");
B:File file = new File("e:\\demo","a.txt");
C:File file = new File("e:\\demo");
  File file2 = new File(file,"a.txt");
(3)File类的功能
A:创建功能

 *public boolean createNewFile()              创建文件 如果存在这样的文件,就不创建了
 *public boolean mkdir()                           创建文件夹 如果存在这样的文件夹,就不创建了
 *public boolean mkdirs()                          创建文件夹,如果父文件夹不存在,会帮你创建出来

        /**
         * public boolean mkdir()
         * 创建文件夹 如果存在这样的文件夹,就不创建了
         */
        // 需求:我要在e盘目录下创建一个文件夹demo
        File file = new File("e:\\demo");
        System.out.println("mkdir:" + file.mkdir());

        /**
         * public boolean createNewFile()
         * 创建文件 如果存在这样的文件,就不创建了
         */
        // 需求:我要在e盘目录demo下创建一个文件a.txt
        File file2 = new File("e:\\demo\\a.txt");
        System.out.println("createNewFile:" + file2.createNewFile());

        /**
         * public boolean mkdirs()
         * 创建文件夹,如果父文件夹不存在,会帮你创建出来
         */
        // 需求:我要在e盘目录test下创建aaa目录
        File file4 = new File("e:\\aaa\\bbb\\ccc\\ddd");
        System.out.println("mkdir:" + file4.mkdirs());

        // 需求:我要在e盘目录test下创建一个文件b.txt
        // Exception in thread "main" java.io.IOException: 系统找不到指定的路径。
        // 注意:要想在某个目录下创建内容,该目录首先必须存在。
//         File file3 = new File("e:\\test\\b.txt");
//         System.out.println("createNewFile:" + file3.createNewFile());


B:删除功能

 *   public boolean delete()
 *
 * 注意:
 * A:如果你创建文件或者文件夹忘了写盘符路径,那么,默认在项目路径下。
 * B:Java中的删除不走回收站。
 * C:要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹

/ 创建文件
// File file = new File("e:\\a.txt");
// System.out.println("createNewFile:" + file.createNewFile());

// 我不小心写成这个样子了
File file = new File("a.txt");
System.out.println("createNewFile:" + file.createNewFile());

// 继续玩几个
File file2 = new File("aaa\\bbb\\ccc");
System.out.println("mkdirs:" + file2.mkdirs());

File file6 = new File("aaa\\bbb\\ccc");
File file7 = new File("aaa\\bbb");
File file8 = new File("aaa");
System.out.println("delete:" + file6.delete());
System.out.println("delete:" + file7.delete());
System.out.println("delete:" + file8.delete());


C:重命名功能

 *       public boolean renameTo(File dest)
 *         如果路径名相同,就是改名。
 *         如果路径名不同,就是改名并剪切。
 *
 * 路径以盘符开始:绝对路径   c:\\a.txt
 * 路径不以盘符开始:相对路径   a.txt

// 创建一个文件对象
//         File file = new File("林青霞.jpg");
//         System.out.println("create:" + file.createNewFile());
        // // 需求:我要修改这个文件的名称为"东方不败.jpg"
//         File newFile = new File("东方不败.jpg");
//         System.out.println("renameTo:" + file.renameTo(newFile));

        File file2 = new File("东方不败.jpg");
        File newFile2 = new File("e:\\林青霞.jpg");
        System.out.println("renameTo:" + file2.renameTo(newFile2));


D:判断功能

 * public boolean isDirectory()       判断是否是目录
 * public boolean isFile()                 判断是否是文件
 * public boolean exists()                判断是否存在
 * public boolean canRead()           判断是否可读
 * public boolean canWrite()          判断是否可写
 * public boolean isHidden()          判断是否隐藏

// 创建文件对象
File file = new File("a.txt");
File file1 = new File("a.jpg");
System.out.println(file1.createNewFile());

System.out.println("isDirectory:" + file.isDirectory());// false
System.out.println("isFile:" + file.isFile());// true
System.out.println("exists:" + file.exists());// true
System.out.println("canRead:" + file.canRead());// true
System.out.println("canWrite:" + file.canWrite());// true
System.out.println("isHidden:" + file.isHidden());// false

System.out.println("isDirectory:" + file1.isDirectory());// false
System.out.println("isFile:" + file1.isFile());// true
System.out.println("exists:" + file1.exists());// true
System.out.println("canRead:" + file1.canRead());// true
System.out.println("canWrite:" + file1.canWrite());// true
System.out.println("isHidden:" + file1.isHidden());// false


E:获取功能

 * public String getAbsolutePath()         获取绝对路径
 * public String getPath()                        获取相对路径
 * public String getName()                     获取名称
 * public long length()                            获取长度。字节数
 * public long lastModified()                  获取最后一次的修改时间,毫秒值 1529030658301

// 创建文件对象
File file = new File("test.txt");
System.out.println("create:" + file.createNewFile());

System.out.println("getAbsolutePath:" + file.getAbsolutePath()); // D:\workspace\shiki\demo\test.txt
System.out.println("getPath:" + file.getPath()); // demo\test.txt
System.out.println("getName:" + file.getName()); // test.txt
System.out.println("length:" + file.length()); // aa啊  4
System.out.println("lastModified:" + file.lastModified()); // 1529030902787

// 1529030658301
Date d = new Date(1529030658301L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
System.out.println(s);

 

F:高级获取功能

 * public String[] list()           获取指定目录下的所有文件或者文件夹的名称数组
 * public File[] listFiles()        获取指定目录下的所有文件或者文件夹的File数组

        // 指定一个目录
        File file = new File("D:\\workspace\\shiki");

        /**
         * public String[] list()
         * 获取指定目录下的所有文件或者文件夹的名称数组
         */
        String[] strArray = file.list();
        for (String s : strArray) {
            System.out.println(s);
        }
//        .idea
//        common-base-platform
//        controller-platform
//        pom.xml
//        shiki.iml
//        test.txt
        System.out.println("------------");
        
        /**
         * public File[] listFiles()
         * 获取指定目录下的所有文件或者文件夹的File数组
         */
        //
        File[] fileArray = file.listFiles();
        for (File f : fileArray) {
            System.out.println(f);
        }
//        D:\workspace\shiki\.idea
//        D:\workspace\shiki\common-base-platform
//        D:\workspace\shiki\controller-platform
//        D:\workspace\shiki\pom.xml
//        D:\workspace\shiki\shiki.iml
//        D:\workspace\shiki\test.txt


G:过滤器功能

// 封装e判断目录
File file = new File("e:\\");

// 获取该目录下所有文件或者文件夹的File数组
File[] fileArray = file.listFiles();

// 遍历该File数组,得到每一个File对象,然后判断
for (File f : fileArray) {
    // 是否是文件
    if (f.isFile()) {
        // 继续判断是否以.jpg结尾
        if (f.getName().endsWith(".jpg")) {
            // 就输出该文件名称
            System.out.println(f.getName());
        }
    }
}
// 封装e判断目录
File file = new File("e:\\");

// 获取该目录下所有文件或者文件夹的String数组
// public String[] list(FilenameFilter filter)
String[] strArray = file.list(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        // return false;
        // return true;
        // 通过这个测试,我们就知道了,到底把这个文件或者文件夹的名称加不加到数组中,取决于这里的返回值是true还是false
        // 所以,这个的true或者false应该是我们通过某种判断得到的
        return new File(dir, name).isFile() && name.endsWith(".jpg");
    }
});

// 遍历
for (String s : strArray) {
    System.out.println(s);
}


(4)案例:
A:输出指定目录下指定后缀名的文件名称
a:先获取所有的,在遍历的时候判断,再输出
b:先判断,再获取,最后直接遍历输出即可
B:批量修改文件名称

============================================================================

1:递归(理解)

(1)方法定义中调用方法本身的现象
举例:老和尚给小和尚讲故事,我们学编程
(2)递归的注意事项;
A:要有出口,否则就是死递归
B:次数不能过多,否则内存溢出
C:构造方法不能递归使用
(3)递归的案例:
A:递归求阶乘
B:兔子问题
C:递归输出指定目录下所有指定后缀名的文件绝对路径
D:递归删除带内容的目录(小心使用)

 

2:IO流(掌握)

(1)IO用于在设备间进行数据传输的操作
(2)分类:
 * IO流的分类:
 * 流向:
 * 输入流 读取数据
 * 输出流   写出数据
 * 数据类型:
 * 字节流
 * 字节输入流 读取数据     InputStream
 * 字节输出流 写出数据     OutputStream
 * 字符流
 * 字符输入流 读取数据     Reader
 * 字符输出流 写出数据     Writer
 *
 * 注意:一般我们在探讨IO流的时候,如果没有明确说明按哪种分类来说,默认情况下是按照数据类型来分的。
 *
 * 需求:我要往一个文本文件中输入一句话:"hello,io"
 *
 * 分析:
 * A:这个操作最好是采用字符流来做,但是呢,字符流是在字节流之后才出现的,所以,今天我先讲解字节流如何操作。
 * B:由于我是要往文件中写一句话,所以我们要采用字节输出流。
 *
 * 通过上面的分析后我们知道要使用:OutputStream
 * 但是通过查看API,我们发现该流对象是一个抽象类,不能实例化。
 * 所以,我们要找一个具体的子类。
 * 而我们要找的子类是什么名字的呢?这个时候,很简单,我们回想一下,我们是不是要往文件中写东西。
 * 文件是哪个单词:File
 * 然后用的是字节输出流,联起来就是:FileOutputStream
 * 注意:每种基类的子类都是以父类名作为后缀名。
 * XxxOutputStream
 * XxxInputStream
 * XxxReader
 * XxxWriter
 * 查看FileOutputStream的构造方法:
 * FileOutputStream(File file)
 * FileOutputStream(String name)
 *
 * 字节输出流操作步骤:
 * A:创建字节输出流对象
 * B:写数据
 * C:释放资源


(3)FileOutputStream写出数据
A:操作步骤
a:创建字节输出流对象

                                FileOutputStream fos = new FileOutputStream("fos.txt");
b:调用write()方法

                                fos.write("hello".getBytes());

                             * public void write(int b):写一个字节
                             * public void write(byte[] b):写一个字节数组
                             * public void write(byte[] b,int off,int len):写一个字节数组的一部分
c:释放资源
        fos.close();
B:代码体现:

// 创建字节输出流对象
      FileOutputStream fos = new FileOutputStream("fos.txt");

/*
 * 创建字节输出流对象了做了几件事情:
 * A:调用系统功能去创建文件
 * B:创建fos对象
 * C:把fos对象指向这个文件
 */

      //写数据
      fos.write("hello,IO".getBytes());
      fos.write("java".getBytes());

      //释放资源
      //关闭此文件输出流并释放与此流有关的所有系统资源。
      fos.close();
/*
 * 为什么一定要close()呢?
 * A:让流对象变成垃圾,这样就可以被垃圾回收器回收了
 * B:通知系统去释放跟该文件相关的资源
 */
      //java.io.IOException: Stream Closed
      //fos.write("java".getBytes());		

C:要注意的问题?
a:创建字节输出流对象做了几件事情?
b:为什么要close()?

                                       * A:让流对象变成垃圾,这样就可以被垃圾回收器回收了
                                       * B:通知系统去释放跟该文件相关的资源

c:如何实现数据的换行?

                             * windows:\r\n
                             * linux:\n
                             * Mac:\r

// 写数据
for (int x = 0; x < 10; x++) {
    fos.write(("hello" + x).getBytes());
    fos.write("\r\n".getBytes());
}

d:如何实现数据的追加写入?

                                 * 用构造方法带第二个参数true的情况即可

// 创建字节输出流对象
// FileOutputStream fos = new FileOutputStream("fos3.txt");
// 创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。
FileOutputStream fos = new FileOutputStream("fos3.txt", true);

// 写数据
for (int x = 0; x < 10; x++) {
    fos.write(("hello" + x).getBytes());
    fos.write("\r\n".getBytes());
}

// 释放资源
fos.close();

加了异常:

// 为了在finally里面能够看到该对象就必须定义到外面,为了访问不出问题,还必须给初始化值
FileOutputStream fos = null;
try {
    // fos = new FileOutputStream("z:\\fos4.txt");
    fos = new FileOutputStream("fos4.txt");
    fos.write("java".getBytes());
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    // 如果fos不是null,才需要close()
    if (fos != null) {
        // 为了保证close()一定会执行,就放到这里了
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(4)FileInputStream读取数据
A:操作步骤
a:创建字节输入流对象
b:调用read()方法

                                    * A:int read():一次读取一个字节
                                    * B:int read(byte[] b):一次读取一个字节数组
c:释放资源

                         
B:代码体现:
FileInputStream fis = new FileInputStream("fos.txt");

//方式1
int by = 0;
while((by=fis.read())!=-1) {
System.out.print((char)by);
}

// FileInputStream(String name)
// FileInputStream fis = new FileInputStream("fis.txt");
FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java");

// 最终版代码
int by = 0;
// 读取,赋值,判断
while ((by = fis.read()) != -1) {
    System.out.print((char) by);
}

// 释放资源
fis.close();

//方式2
byte[] bys = new byte[1024];
int len = 0;
while((len=fis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));
}

// 创建字节输入流对象
// FileInputStream fis = new FileInputStream("fis2.txt");
FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java");

// 最终版代码
// 数组的长度一般是1024或者1024的整数倍
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
    System.out.print(new String(bys, 0, len));
}

// 释放资源
fis.close();


fis.close();
(5)案例:2种实现
A:复制文本文件

// 封装数据源 必须存在该文件
FileInputStream fis = new FileInputStream("a.txt");
// 封装目的地 该文件可以不存在
FileOutputStream fos = new FileOutputStream("b.txt");

int by = 0;
while ((by = fis.read()) != -1) {
    fos.write(by);
}

// 释放资源(先关谁都行)
fos.close();
fis.close();

 

B:复制图片

// 封装数据源
FileInputStream fis = new FileInputStream("e:\\林青霞.jpg");
// 封装目的地
FileOutputStream fos = new FileOutputStream("mn.jpg");

// 复制数据
int by = 0;
while ((by = fis.read()) != -1) {
    fos.write(by);
}

// 释放资源
fos.close();
fis.close();


C:复制视频

// 封装数据源
FileInputStream fis = new FileInputStream("e:\\hehe.mp4");
// 封装目的地
FileOutputStream fos = new FileOutputStream("copy.mp4");

// 复制数据
int by = 0;
while ((by = fis.read()) != -1) {
    fos.write(by);
}

// 释放资源
fos.close();
fis.close();


(6)字节缓冲区流
A:BufferedOutputStream

// 简单写法
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

// 写数据
bos.write("hello".getBytes());

// 释放资源
bos.close();

 

B:BufferedInputStream

// BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt"));

// 读取数据
// int by = 0;
// while ((by = bis.read()) != -1) {
// System.out.print((char) by);
// }
// System.out.println("---------");

byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
    System.out.print(new String(bys, 0, len));
}

// 释放资源
bis.close();


(7)案例:4种实现
A:复制文本文件
B:复制图片
C:复制视频

3:自学字符流

IO流分类
字节流:
InputStream
FileInputStream
BufferedInputStream
OutputStream
FileOutputStream
BufferedOutputStream

字符流:
Reader
FileReader
BufferedReader
Writer
FileWriter
BufferedWriter

============================================================================

1:字符流(掌握)

(1)字节流操作中文数据不是特别的方便,所以就出现了转换流
   转换流的作用就是把字节流转换字符流来使用
(2)转换流其实是一个字符流
字符流 = 字节流 + 编码表
(3)编码表
A:就是由字符和对应的数值组成的一张表
B:常见的编码表
ASCII
ISO-8859-1
GB2312
GBK
GB18030
UTF-8
C:字符串中的编码问题
编码
String -- byte[]
解码
byte[] -- String
(4)IO流中的编码问题
A:OutputStreamWriter
OutputStreamWriter(OutputStream os):默认编码,GBK
OutputStreamWriter(OutputStream os,String charsetName):指定编码。
B:InputStreamReader
InputStreamReader(InputStream is):默认编码,GBK
InputStreamReader(InputStream is,String charsetName):指定编码
C:编码问题其实很简单
编码只要一致即可
(5)字符流
Reader
|--InputStreamReader
|--FileReader
|--BufferedReader
Writer
|--OutputStreamWriter
|--FileWriter
|--BufferedWriter
(6)复制文本文件(5种方式)
 

2:IO流小结(掌握)

IO流
|--字节流
|--字节输入流
InputStream
int read():一次读取一个字节
int read(byte[] bys):一次读取一个字节数组

|--FileInputStream
|--BufferedInputStream
|--字节输出流
OutputStream
void write(int by):一次写一个字节
void write(byte[] bys,int index,int len):一次写一个字节数组的一部分

|--FileOutputStream
|--BufferedOutputStream
|--字符流
|--字符输入流
Reader
int read():一次读取一个字符
int read(char[] chs):一次读取一个字符数组

|--InputStreamReader
|--FileReader
|--BufferedReader
String readLine():一次读取一个字符串
|--字符输出流
Writer
void write(int ch):一次写一个字符
void write(char[] chs,int index,int len):一次写一个字符数组的一部分

|--OutputStreamWriter
|--FileWriter
|--BufferedWriter
void newLine():写一个换行符

void write(String line):一次写一个字符串
 

3:案例(理解 练习一遍)

A:复制文本文件 5种方式(掌握)
B:复制图片(二进制流数据) 4种方式(掌握)
C:把集合中的数据存储到文本文件
D:把文本文件中的数据读取到集合并遍历集合
E:复制单级文件夹
F:复制单级文件夹中指定的文件并修改名称
回顾一下批量修改名称
G:复制多级文件夹
H:键盘录入学生信息按照总分从高到低存储到文本文件
I:把某个文件中的字符串排序后输出到另一个文本文件中
J:用Reader模拟BufferedReader的特有功能
K:模拟LineNumberReader的特有功能

============================================================================

1:登录注册IO版本案例(掌握)

要求,对着写一遍。

cn.itcast.pojo User
cn.itcast.dao UserDao
cn.itcast.dao.impl UserDaoImpl(实现我不管)
cn.itcast.game GuessNumber
cn.itcast.test UserTest
 

2:数据操作流(操作基本类型数据的流)(理解)

(1)可以操作基本类型的数据
(2)流对象名称
DataInputStream
DataOutputStream
 

3:内存操作流(理解)

(1)有些时候我们操作完毕后,未必需要产生一个文件,就可以使用内存操作流。
(2)三种
A:ByteArrayInputStream,ByteArrayOutputStream
B:CharArrayReader,CharArrayWriter
C:StringReader,StringWriter
 

4:打印流(掌握)

(1)字节打印流,字符打印流
(2)特点:
A:只操作目的地,不操作数据源
B:可以操作任意类型的数据
C:如果启用了自动刷新,在调用println()方法的时候,能够换行并刷新
D:可以直接操作文件
问题:哪些流可以直接操作文件呢?
看API,如果其构造方法能够同时接收File和String类型的参数,一般都是可以直接操作文件的
(3)复制文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("b.txt"),true);

String line = null;
while((line=br.readLine())!=null) {
pw.println(line);
}

pw.close();
br.close();

5:标准输入输出流(理解)

(1)System类下面有这样的两个字段
in 标准输入流
out 标准输出流
(2)三种键盘录入方式
A:main方法的args接收参数
B:System.in通过BufferedReader进行包装
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
C:Scanner
Scanner sc = new Scanner(System.in);
(3)输出语句的原理和如何使用字符流输出数据
A:原理
System.out.println("helloworld");

PrintStream ps = System.out;
ps.println("helloworld");
B:把System.out用字符缓冲流包装一下使用
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
 

6:随机访问流(理解)

(1)可以按照文件指针的位置写数据和读数据。
(2)案例:
A:写数据
B:读数据
C:获取和改变文件指针的位置
 

7:合并流(理解)

(1)把多个输入流的数据写到一个输出流中。
(2)构造方法:
A:SequenceInputStream(InputStream s1, InputStream s2) 
B:SequenceInputStream(Enumeration<? extends InputStream> e) 
 

8:序列化流(理解)

(1)可以把对象写入文本文件或者在网络中传输
(2)如何实现序列化呢?
让被序列化的对象所属类实现序列化接口。
该接口是一个标记接口。没有功能需要实现。
(3)注意问题:
把数据写到文件后,在去修改类会产生一个问题。
如何解决该问题呢?
在类文件中,给出一个固定的序列化id值。
而且,这样也可以解决黄色警告线问题
(4)面试题:
什么时候序列化?
如何实现序列化?
什么是反序列化?
 

9:Properties(理解)

(1)是一个集合类,Hashtable的子类
(2)特有功能
A:public Object setProperty(String key,String value)
B:public String getProperty(String key)
C:public Set<String> stringPropertyNames()
(3)和IO流结合的方法
把键值对形式的文本文件内容加载到集合中
public void load(Reader reader)
public void load(InputStream inStream)


把集合中的数据存储到文本文件中
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
(4)案例:
A:根据给定的文件判断是否有键为"lisi"的,如果有就修改其值为100
B:写一个程序实现控制猜数字小游戏程序不能玩超过5次
 

10:NIO(了解)

(1)JDK4出现的NIO,对以前的IO操作进行了优化,提供了效率。但是大部分我们看到的还是以前的IO
(2)JDK7的NIO的使用
Path:路径
Paths:通过静态方法返回一个路径
Files:提供了常见的功能
复制文本文件
把集合中的数据写到文本文件

============================================================================

1:多线程(理解)

(1)多线程:一个应用程序有多条执行路径


进程:正在执行的应用程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
线程进程的执行单元,执行路径。是进程中的单个顺序控制流,是一条执行路径
单线程:一个应用程序只有一条执行路径
多线程:一个应用程序有多条执行路径

多进程的意义?
提高CPU的使用率
多线程的意义?
提高应用程序的使用率


 *  举例:
 *   扫雷程序,迅雷下载
 
                并行和并发:
 *         并行:前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
 *         并发:后者是物理上同时发生,指在某一个时间点同时运行多个程序。
 

(2)Java程序的运行原理及JVM的启动是多线程的吗?
A:由java命令启动JVM,JVM启动就相当于启动了一个进程。接着有该进程创建了一个主线程去调用main方法。。
B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程
(3)多线程的实现方案(自己补齐步骤及代码 掌握)
A:继承Thread类

步骤:
 * A:自定义类MyThread继承Thread类。

 * B:MyThread类里面重写run()?
  为什么是run()方法呢?

            答:

                * 不是类中的所有代码都需要被线程执行的。
                * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。

 * C:创建对象
 * D:启动线程

run()和start()的区别?
        run():仅仅是封装被线程执行的代码,直接调用是普通方法
        start():首先启动了线程,然后再由jvm去调用该线程的run()方法。

/*
 * 该类要重写run()方法,为什么呢?
 * 不是类中的所有代码都需要被线程执行的。
 * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
 */
public class b_MyThread extends Thread{
    @Override
    public void run() {
        // 自己写代码
        // System.out.println("好好学习,天天向上");
        // 一般来说,被线程执行的代码肯定是比较耗时的。所以我们用循环改进
        for (int x = 0; x < 200; x++) {
            System.out.println(x);
        }
    }
}
// 创建线程对象
// MyThread my = new MyThread();
// // 启动线程
// my.run();
// my.run();
// 调用run()方法为什么是单线程的呢?
// 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
// 要想看到多线程的效果,就必须说说另一个方法:start()
// 面试题:run()和start()的区别?
// run():仅仅是封装被线程执行的代码,直接调用是普通方法
// start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
// MyThread my = new MyThread();
// my.start();
// // IllegalThreadStateException:非法的线程状态异常
// // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
// my.start();

// 创建两个线程对象
b_MyThread my1 = new b_MyThread();
b_MyThread my2 = new b_MyThread();

my1.start();
my2.start();


 * public final String getName()                    :获取线程的名称。
 * public final void setName(String name)    :设置线程的名称
 
 * 针对不是Thread类的子类中如何获取线程对象名称呢?
 * public static Thread currentThread()            :返回当前正在执行的线程对象
 * Thread.currentThread().getName()

   // 创建线程对象
        /**
         * 方式一:无参构造+setXxx()
         */
//        c_MyThread my1 = new c_MyThread();
//        c_MyThread my2 = new c_MyThread();
//         //调用方法设置名称
//         my1.setName("线程一");
//         my2.setName("线程二");
//         my1.start();
//         my2.start();

        /**
         * 方式二:带参构造方法给线程起名字
         */
        c_MyThread my1 = new c_MyThread("线程一");
        c_MyThread my2 = new c_MyThread("线程二");
         my1.start();
         my2.start();

        //我要获取main方法所在的线程对象的名称,该怎么办呢?
        //遇到这种情况,Thread类提供了一个很好玩的方法:
        /**
         * public static Thread currentThread():返回当前正在执行的线程对象
         */
        System.out.println(Thread.currentThread().getName());

                B:实现Runnable接口

 * 方式2:实现Runnable接口
 * 步骤:
 * A:自定义类MyRunnable实现Runnable接口
 * B:重写run()方法
 * C:创建MyRunnable类的对象
 * D:创建Thread类的对象,并把C步骤的对象作为构造参数传递

public class a_MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}

 

// 创建MyRunnable类的对象
a_MyRunnable my = new a_MyRunnable();

// 创建Thread类的对象,并把C步骤的对象作为构造参数传递
// Thread(Runnable target)
// Thread t1 = new Thread(my);
// Thread t2 = new Thread(my);
// t1.setName("林青霞");
// t2.setName("刘意");

// Thread(Runnable target, String name)
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "刘意");

t1.start();
t2.start();


(4)线程的调度和优先级问题
A:线程的调度
a:分时调度
b:抢占式调度 (Java采用的是该调度方式)
B:获取和设置线程优先级
a:默认是5
b:范围是1-10  数字越大 越先执行

 * 我们的线程没有设置优先级,肯定有默认优先级。
 * 那么,默认优先级是多少呢?
 * 如何获取线程对象的优先级?
 * public final int getPriority():返回线程对象的优先级
 * 如何设置线程对象的优先级呢?
 * public final void setPriority(int newPriority):更改线程的优先级。
 *
 * 注意:
 * 线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
 *
 * IllegalArgumentException:非法参数异常。
 * 抛出的异常表明向方法传递了一个不合法或不正确的参数。

d_ThreadPriority tp1 = new d_ThreadPriority();
        d_ThreadPriority tp2 = new d_ThreadPriority();
        d_ThreadPriority tp3 = new d_ThreadPriority();

        tp1.setName("东方不败");
        tp2.setName("岳不群");
        tp3.setName("林平之");

        // 获取默认优先级 5
        System.out.println(tp1.getPriority()); // 5
        System.out.println(tp2.getPriority()); // 5
        System.out.println(tp3.getPriority()); // 5

        // 设置线程优先级 只能在1-10之间
//        tp1.setPriority(100000); // IllegalArgumentException:非法参数异常。

        //设置正确的线程优先级
        tp1.setPriority(1);
        tp2.setPriority(2);
        tp3.setPriority(10);

        tp1.start();
        tp2.start();
        tp3.start();


(5)线程的控制(常见方法)
A:休眠线程

                        public static void sleep(long millis)

                                Thread.sleep(1000); // 一秒钟

@Override
public void run() {
    for (int x = 0; x < 100; x++) {
        System.out.println(getName() + ":" + x + ",日期:" + new Date());
        // 睡眠
        // 困了,我稍微休息1秒钟
        try {
            Thread.sleep(1000); // 一秒钟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

B:加入线程


C:礼让线程

 * public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
 * 让多个线程的执行更和谐,但是不能靠它保证一人一次。

@Override
public void run() {
    for (int x = 0; x < 1000; x++) {
        System.out.println(getName() + ":" + x);
        Thread.yield();
    }
}
f_YieldThread ty1 = new f_YieldThread();
f_YieldThread ty2 = new f_YieldThread();

ty1.setName("哈哈");
ty2.setName("呱呱");

ty1.start();
ty2.start();

D:后台线程
E:终止线程(掌握)

public final void join():等待该线程终止。

e_JoinThread tj1 = new e_JoinThread();
e_JoinThread tj2 = new e_JoinThread();
e_JoinThread tj3 = new e_JoinThread();

tj1.setName("李渊");
tj2.setName("李世民");
tj3.setName("李元霸");

tj1.start();
try {
    tj1.join(); // 等待该线程终止
} catch (InterruptedException e) {
    e.printStackTrace();
}

tj2.start();
tj3.start();

F:中断线程

 * public final void stop():让线程停止,过时了,但是还可以使用。
 * public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。

@Override
public void run() {
    System.out.println("开始执行:" + new Date());

    // 我要休息10秒钟,亲,不要打扰我哦
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        // e.printStackTrace();
        System.out.println("线程被终止了");
    }

    System.out.println("结束执行:" + new Date());
}
h_StopThread ts = new h_StopThread();
ts.start();

// 你超过三秒不醒过来,我就干死你
try {
    Thread.sleep(3000);
    // ts.stop();
    ts.interrupt();
} catch (InterruptedException e) {
    e.printStackTrace();
}

G:守护线程

 * public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
 * 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。
 *
 * 游戏:坦克大战。

@Override
public void run() {
    for (int x = 0; x < 100; x++) {
        System.out.println(getName() + ":" + x);
    }
}
i_DaemonThread td1 = new i_DaemonThread();
i_DaemonThread td2 = new i_DaemonThread();

td1.setName("关羽");
td2.setName("张飞");

// 设置守护线程
td1.setDaemon(true);
td2.setDaemon(true);

td1.start();
td2.start();

Thread.currentThread().setName("刘备"); // main
for (int x = 0; x < 5; x++) {
    System.out.println(Thread.currentThread().getName() + ":" + x);
}


(6)线程的生命周期(参照 线程生命周期图解.bmp)
A:新建
B:就绪
C:运行
D:阻塞
E:死亡
(7)电影院卖票程序的实现
A:继承Thread类

// 定义100张票
// private int tickets = 100;
// 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
private static int tickets = 100;

@Override
public void run() {
    // 定义100张票
    // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
    // int tickets = 100;

    // 是为了模拟一直有票
    while (true) {
        if (tickets > 0) {
            System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
        }
    }
}
// 创建三个线程对象
a_extendThread st1 = new a_extendThread();
a_extendThread st2 = new a_extendThread();
a_extendThread st3 = new a_extendThread();

// 给线程对象起名字
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");

// 启动线程
st1.start();
st2.start();
st3.start();


B:实现Runnable接口

// 定义100张票
private int tickets = 100;

@Override
public void run() {
    while (true) {
        if (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
        }
    }
}
// 创建资源对象
b_implRunnable st = new b_implRunnable();

// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();


(8)电影院卖票程序出问题
A:为了更符合真实的场景,加入了休眠100毫秒。
B:卖票问题
a:同票多次
b:负数票

public class c_implRunnable implements Runnable{
    // 定义100张票
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
            // t1,t2,t3三个线程
            // 这一次的tickets = 1;
            if (tickets > 0) {
                // 为了模拟更真实的场景,我们稍作休息
                try {
                    Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "正在出售第"
                        + (tickets--) + "张票");
                //窗口1正在出售第1张票,tickets=0
                //窗口2正在出售第0张票,tickets=-1
                //窗口3正在出售第-1张票,tickets=-2
            }
        }
    }
}
/*
 * 实现Runnable接口的方式实现
 *
 * 通过加入延迟后,就产生了连个问题:
 * A:相同的票卖了多次
 *        CPU的一次操作必须是原子性的
 * B:出现了负数票
 *      窗口3正在出售第-1张票
 *        随机性和延迟导致的
 */
public class c_implRunnableDemo_somethingwrong {
    public static void main(String[] args) {
        // 创建资源对象
        c_implRunnable st = new c_implRunnable();

        // 创建三个线程对象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 启动线程
        t1.start();
        t2.start();
        t3.start();

        // 窗口3正在出售第-1张票
    }
}


(9)多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据


(10)同步解决线程安全问题
A:同步代码块
synchronized(对象) {
需要被同步的代码;
}


这里的锁对象可以是任意对象。

public class e_synchronized implements Runnable{
    // 定义100张票
    private int tickets = 100;

    // 定义同一把锁
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            // t1,t2,t3都能走到这里
            // 假设t1抢到CPU的执行权,t1就要进来
            // 假设t2抢到CPU的执行权,t2就要进来,发现门是关着的,进不去。所以就等着。
            // 门(开,关)
            synchronized (obj) { // 发现这里的代码将来是会被锁上的,所以t1进来后,就锁了。(关)
                if (tickets > 0) {
                    try {
                        Thread.sleep(100); // t1就睡眠了
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "张票 ");
                    //窗口1正在出售第100张票
                }
            } //t1就出来了,然后就开门。(开)
        }
    }
}
/*
 * 举例:
 *        火车上厕所。
 *
 * 同步的特点:
 *        前提:
 *           多个线程
 *    解决问题的时候要注意:
 *       多个线程使用的是同一个锁对象
 * 同步的好处
 *    同步的出现解决了多线程的安全问题。
 * 同步的弊端
 *    当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
 */
public class e_synchronizedDemo {
    public static void main(String[] args) {
        // 创建资源对象
        e_synchronized st = new e_synchronized();

        // 创建三个线程对象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}


B:同步方法
把同步加在方法上。
这里的锁对象是this

C:静态同步方法
把同步加在方法上。

这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)
(11)回顾以前的线程安全的类
A:StringBuffer
B:Vector
C:Hashtable

D:如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全

// 线程安全的类
StringBuffer sb = new StringBuffer();
Vector<String> v = new Vector<String>();
Hashtable<String, String> h = new Hashtable<String, String>();

// Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你
// 那么到底用谁呢?
// public static <T> List<T> synchronizedList(List<T> list)
List<String> list1 = new ArrayList<String>();// 线程不安全
List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全

 

============================================================================

1:多线程(理解)

(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁

 * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
 * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
 *
 * Lock:
 * void lock(): 获取锁。
 * void unlock():释放锁。
 * ReentrantLock是Lock的实现类.

public class a_SellTicket implements Runnable{
    // 定义票
    private int tickets = 100;

    // 定义锁对象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                // 加锁
                lock.lock();
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "张票");
                }
            } finally {
                // 释放锁
                lock.unlock();
            }
        }
    }
}
// 创建资源对象
a_SellTicket st = new a_SellTicket();

// 创建三个窗口
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();


(2)死锁问题的描述和代码体现

 * 同步的弊端:
 * A:效率低
 * B:容易产生死锁

 *
 * 死锁:
 * 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。

public class b_DieLock extends Thread{
    private boolean flag;

    public b_DieLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (b_MyLock.objA) {
                System.out.println("if objA");
                synchronized (b_MyLock.objB) {
                    System.out.println("if objB");
                }
            }
        } else {
            synchronized (b_MyLock.objB) {
                System.out.println("else objB");
                synchronized (b_MyLock.objA) {
                    System.out.println("else objA");
                }
            }
        }
    }
}
/*
 * 同步的弊端:
 *        A:效率低
 *        B:容易产生死锁
 *
 * 死锁:
 *        两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。
 *
 * 举例:
 *        中国人,美国人吃饭案例。
 *        正常情况:
 *           中国人:筷子两支
 *           美国人:刀和叉
 *        现在:
 *           中国人:筷子1支,刀一把
 *           美国人:筷子1支,叉一把
 */
public class b_DieLockDemo{
    public static void main(String[] args) {
        b_DieLock dl1 = new b_DieLock(true);
        b_DieLock dl2 = new b_DieLock(false);

        dl1.start();
        dl2.start();
    }
}
/**
 * 锁对象
 */
public class b_MyLock{
    // 创建两把锁对象
    public static final Object objA = new Object();
    public static final Object objB = new Object();
}


(3)生产者和消费者多线程体现(线程间通信问题)
以学生作为资源来实现的

资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo

代码:
A:最基本的版本,只有一个数据。

资源类:Student

public class Student {
    String name;
    int age;
}


设置数据类:SetThread(生产者)

public class SetThread implements Runnable {

    private Student s;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        // Student s = new Student();
        s.name = "林青霞";
        s.age = 27;
    }
}


获取数据类:GetThread(消费者)

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        // Student s = new Student();
        System.out.println(s.name + "---" + s.age);
    }

}


测试类:StudentDemo

/*
 * 分析:
 * 		资源类:Student
 * 		设置学生数据:SetThread(生产者)
 * 		获取学生数据:GetThread(消费者)
 * 		测试类:StudentDemo
 *
 * 问题1:按照思路写代码,发现数据每次都是:null---0
 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
 * 如何实现呢?
 * 		在外界把这个数据创建出来,通过构造方法传递给其他的类。
 *
 */
public class StudentDemo {
    public static void main(String[] args) {
        //创建资源
        Student s = new Student();

        //设置和获取的类
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);

        //线程类
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);

        //启动线程
        t1.start();
        t2.start();
    }
}


B:改进版本,给出了不同的数据,并加入了同步机制

public class Student {
    String name;
    int age;
}
public class SetThread implements Runnable {

    private Student s;
    private int x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if (x % 2 == 0) {
                    s.name = "哈哈";//刚走到这里,就被别人抢到了执行权
                    s.age = 27;
                } else {
                    s.name = "嘻嘻"; //刚走到这里,就被别人抢到了执行权
                    s.age = 30;
                }
                x++;
            }
        }
    }
}
public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                System.out.println(s.name + "---" + s.age);
            }
        }
    }
}
public static void main(String[] args) {
        //创建资源
        Student s = new Student();

        //设置和获取的类
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);

        //线程类
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);

        //启动线程
        t1.start();
        t2.start();
    }


C:等待唤醒机制改进该程序,让数据能够实现依次的出现

* 分析:
*     资源类:Student
*     设置学生数据:SetThread(生产者)
*     获取学生数据:GetThread(消费者)
*     测试类:StudentDemo
*
* 问题1:按照思路写代码,发现数据每次都是:null---0
* 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
* 如何实现呢?
*     在外界把这个数据创建出来,通过构造方法传递给其他的类。
*
* 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
*     A:同一个数据出现多次
*     B:姓名和年龄不匹配
* 原因:
*     A:同一个数据出现多次
*        CPU的一点点时间片的执行权,就足够你执行很多次。
*     B:姓名和年龄不匹配
*        线程运行的随机性
* 线程安全问题:
*     A:是否是多线程环境    是
*     B:是否有共享数据     是
*     C:是否有多条语句操作共享数据    是
* 解决方案:
*     加锁。
*     注意:
*        A:不同种类的线程都要加锁。
*        B:不同种类的线程加的锁必须是同一把。
*
* 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
* 如何实现呢?
*     通过Java提供的等待唤醒机制解决。
*
* 等待唤醒:
*     Object类中提供了三个方法:
*        wait():等待
*        notify():唤醒单个线程
*        notifyAll():唤醒所有线程
*     为什么这些方法不定义在Thread类中呢?
*        这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
*        所以,这些方法必须定义在Object类中。
*
* 最终版代码中:
*     把Student的成员变量给私有的了。
*     把设置和获取的操作给封装成了功能,并加了同步。
*     设置或者获取的线程里面只需要调用方法即可。

资源类:Student

    String name;
    int age;
    boolean flag; // 默认情况是没有数据,如果是true,说明有数据


设置数据类:SetThread(生产者)

public class SetThread implements Runnable {

    private Student s;
    private int x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                //判断有没有
                if(s.flag){
                    try {
                        s.wait(); //t1等着,释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if (x % 2 == 0) {
                    s.name = "哈哈";
                    s.age = 27;
                } else {
                    s.name = "嘻嘻";
                    s.age = 30;
                }
                x++; //x=1

                //修改标记
                s.flag = true;
                //唤醒线程
                s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
            }
            //t1有,或者t2有
        }
    }
}


获取数据类:GetThread(消费者)

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if(!s.flag){
                    try {
                        s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(s.name + "---" + s.age);
                //林青霞---27
                //刘意---30

                //修改标记
                s.flag = false;
                //唤醒线程
                s.notify(); //唤醒t1
            }
        }
    }
}


测试类:StudentDemo

public static void main(String[] args) {
        //创建资源
        Student s = new Student();

        //设置和获取的类
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);

        //线程类
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);

        //启动线程
        t1.start();
        t2.start();
    }

wait()
notify()
notifyAll() (多生产多消费)
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中(最终版)

资源类:Student

public class Student {
    private String name;
    private int age;
    private boolean flag; // 默认情况是没有数据,如果是true,说明有数据

    public synchronized void set(String name, int age) {
        // 如果有数据,就等待
        if (this.flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 设置数据
        this.name = name;
        this.age = age;

        // 修改标记
        this.flag = true;
        this.notify();
    }

    public synchronized void get() {
        // 如果没有数据,就等待
        if (!this.flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 获取数据
        System.out.println(this.name + "---" + this.age);

        // 修改标记
        this.flag = false;
        this.notify();
    }
}

设置数据类:SetThread(生产者)

public class SetThread implements Runnable {

    private Student s;
    private int x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
                s.set("张三", 27);
            } else {
                s.set("李四", 30);
            }
            x++;
        }
    }
}


获取数据类:GetThread(消费者)

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            s.get();
        }
    }
}


测试类:StudentDemo

public static void main(String[] args) {
        //创建资源
        Student s = new Student();

        //设置和获取的类
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);

        //线程类
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);

        //启动线程
        t1.start();
        t2.start();
    }


(4)线程组

* 线程组: 把多个线程组合到一起。
* 线程类里面的方法:public final ThreadGroup getThreadGroup()
* 线程组里面的方法:public final String getName()
* ThreadGroup(String name)
* Thread(ThreadGroup group, Runnable target, String name)
* 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
* 通过结果我们知道了:线程默认情况下属于main线程组
* 默任情况下,所有的线程都属于同一个组
        MyRunnable my = new MyRunnable();
        Thread t1 = new Thread(my, "哈哈");
        Thread t2 = new Thread(my, "嘻嘻");
        // 我不知道他们属于那个线程组,我想知道,怎么办
        // 线程类里面的方法:public final ThreadGroup getThreadGroup()
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        // 线程组里面的方法:public final String getName()
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        System.out.println(name1);
        System.out.println(name2);
        // 通过结果我们知道了:线程默认情况下属于main线程组
        // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组
        System.out.println(Thread.currentThread().getThreadGroup().getName());
        /**
         * ThreadGroup(String name)
         */
        ThreadGroup tg = new ThreadGroup("这是一个新的组");

        MyRunnable my = new MyRunnable();
        /**
         * Thread(ThreadGroup group, Runnable target, String name)
         */
        Thread t1 = new Thread(tg, my, "哈哈");
        Thread t2 = new Thread(tg, my, "嘻嘻");

        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());

        //通过组名称设置后台线程,表示该组的线程都是后台线程
        tg.setDaemon(true);

(5)线程池

* 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
* 
* 如何实现线程的代码呢?
*     A:创建一个线程池对象,控制要创建几个线程对象。
*        public static ExecutorService newFixedThreadPool(int nThreads)
*     B:这种线程池的线程可以执行:
*        可以执行Runnable对象或者Callable对象代表的线程
*        做一个类实现Runnable接口。
*     C:调用如下方法即可
*        Future<?> submit(Runnable task)
*        <T> Future<T> submit(Callable<T> task)
*     D:我就要结束,可以吗?
*        可以。
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}
public static void main(String[] args) {
        // 创建一个线程池对象,控制要创建几个线程对象。
        // public static ExecutorService newFixedThreadPool(int nThreads)
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        //结束线程池
        pool.shutdown();
    }

(6)多线程实现的第三种方案

* 多线程实现的方式3:
*      A:创建一个线程池对象,控制要创建几个线程对象。
*        public static ExecutorService newFixedThreadPool(int nThreads)
*     B:这种线程池的线程可以执行:
*        可以执行Runnable对象或者Callable对象代表的线程
*        做一个类实现Runnable接口。
*     C:调用如下方法即可
*        Future<?> submit(Runnable task)
*        <T> Future<T> submit(Callable<T> task)
*     D:我就要结束,可以吗?
*        可以。
public class MyCallable implements Callable{
    @Override
    public Object call() throws Exception {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
        return null;
    }
}
        //创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //可以执行Runnable对象或者Callable对象代表的线程
        pool.submit(new MyCallable());
        pool.submit(new MyCallable());

        //结束
        pool.shutdown();

===========线程求和案例============:

public class MyCallable implements Callable<Integer> {

    private int number;

    public MyCallable(int number) {
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int x = 1; x <= number; x++) {
            sum += x;
        }
        return sum;
    }

}
        // 创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程
        Future<Integer> f1 = pool.submit(new MyCallable(2));
        Future<Integer> f2 = pool.submit(new MyCallable(3));
        Future<Integer> f3 = pool.submit(new MyCallable(4));

        // V get()
        Integer i1 = f1.get();
        Integer i2 = f2.get();
        Integer i3 = f3.get();

        System.out.println(i1); // 3
        System.out.println(i2); // 6
        System.out.println(i3); // 10

        // 结束
        pool.shutdown();

(6)匿名内部类的格式

* 匿名内部类的格式:
*     new 类名或者接口名() {
*        重写方法;
*     };
*     本质:是该类或者接口的子类对象。
/*
 * 匿名内部类的格式:
 * 		new 类名或者接口名() {
 * 			重写方法;
 * 		};
 * 		本质:是该类或者接口的子类对象。
 */
public class ThreadDemo {
    public static void main(String[] args) {
        // 继承Thread类来实现多线程
        new Thread() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println(Thread.currentThread().getName() + ":" + x);
                }
            }
        }.start();

        // 实现Runnable接口来实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println(Thread.currentThread().getName() + ":" + x);
                }
            }
        }) {
        }.start();

        // 更有难度的
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println("hello" + ":" + x);
                }
            }
        }) {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println("world" + ":" + x);
                }
            }
        }.start();
    }
}

(7)定时器

* 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
* 依赖Timer和TimerTask这两个类:
* Timer:定时
*     public Timer()
*     public void schedule(TimerTask task,long delay)
*     public void schedule(TimerTask task,long delay,long period)
*     public void cancel()
* TimerTask:任务
public class TimerDemo {
    public static void main(String[] args) {
        // 创建定时器对象
        Timer t = new Timer();
        // 3秒后执行爆炸任务
        // t.schedule(new MyTask(), 3000);
        //结束任务
        t.schedule(new MyTask(t), 3000);
    }
}

// 做一个任务
class MyTask extends TimerTask {

    private Timer t;

    public MyTask(){}

    public MyTask(Timer t){
        this.t = t;
    }

    @Override
    public void run() {
        System.out.println("bang,爆炸了");
        t.cancel();
    }
}
public class TimerDemo2 {
    public static void main(String[] args) {
        // 创建定时器对象
        Timer t = new Timer();
        // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸
        t.schedule(new MyTask2(), 3000, 2000);
    }
}

// 做一个任务
class MyTask2 extends TimerTask {
    @Override
    public void run() {
        System.out.println("bang,爆炸了");
    }
}
/*
 * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo)
 */

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class DeleteFolder extends TimerTask {

    @Override
    public void run() {
        File srcFolder = new File("demo");
        deleteFolder(srcFolder);
    }

    // 递归删除目录
    public void deleteFolder(File srcFolder) {
        File[] fileArray = srcFolder.listFiles();
        if (fileArray != null) {
            for (File file : fileArray) {
                if (file.isDirectory()) {
                    deleteFolder(file);
                } else {
                    System.out.println(file.getName() + ":" + file.delete());
                }
            }
            System.out.println(srcFolder.getName() + ":" + srcFolder.delete());
        }
    }
}

public class TimerTest {
    public static void main(String[] args) throws ParseException {
        Timer t = new Timer();

        String s = "2018-07-17 14:46:00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(s);

        t.schedule(new DeleteFolder(), d);
    }
}

(8)多线程的面试题

1:多线程有几种实现方案,分别是哪几种?
    两种。
    
    继承Thread类
    实现Runnable接口
    
    扩展一种:实现Callable接口。这个得和线程池结合。

2:同步有几种方式,分别是什么?
    两种。
    
    同步代码块
    同步方法

3:启动一个线程是run()还是start()?它们的区别?
    start();
    
    run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用
    start():启动线程,并由JVM自动调用run()方法

4:sleep()和wait()方法的区别
    sleep():必须指时间;不释放锁。
    wait():可以不指定时间,也可以指定时间;释放锁。

5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中
    因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
    而Object代码任意的对象,所以,定义在这里面。

6:线程的生命周期图
    新建 -- 就绪 -- 运行 -- 死亡
    新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡
    建议:画图解释。

2:设计模式(理解)

(1)面试对象的常见设计原则
单一
开闭
里氏
依赖注入
接口
迪米特
(2)设计模式概述和分类
A:经验的总结
B:三类
创建型
结构型
行为型
(3)改进的设计模式
A:简单工厂模式
B:工厂方法模式
C:单例模式(掌握)
a:饿汉式
b:懒汉式
(4)Runtime
JDK提供的一个单例模式应用的类。
还可以调用dos命令。

public static void main(String[] args) throws IOException {
		Runtime r = Runtime.getRuntime();
//		r.exec("winmine");
		r.exec("notepad"); // 打开记事本
		r.exec("calc"); // 打开计算器
//		r.exec("shutdown -s -t 10000");
//		r.exec("shutdown -a");
	}

============================================================================

1:如何让Netbeans的东西Eclipse能访问。

在Eclipse中创建项目,把Netbeans项目的src下的东西给拿过来即可。
注意:修改项目编码为UTF-8

2:GUI(了解)

(1)用户图形界面
GUI:方便直观
CLI:需要记忆一下命令,麻烦
(2)两个包:
java.awt:和系统关联较强
javax.swing:纯Java编写
(3)GUI的继承体系
组件:组件就是对象
容器组件:是可以存储基本组件和容器组件的组件。
基本组件:是可以使用的组件,但是必须依赖容器。
(4)事件监听机制(理解)
A:事件源
B:事件
C:事件处理
D:事件监听
(5)适配器模式(理解)
A:接口
B:抽象适配器类
C:实现类
(6)案例:
A:创建窗体案例
B:窗体关闭案例
C:窗体添加按钮并对按钮添加事件案例。
界面中的组件布局。
D:把文本框里面的数据转移到文本域
E:更改背景色
F:设置文本框里面不能输入非数字字符
G:一级菜单
H:多级菜单
(7)Netbeans的概述和使用
A:是可以做Java开发的另一个IDE工具。
B:使用
A:四则运算
a:修改图标
b:设置皮肤
c:设置居中
d:数据校验
B:登录注册

============================================================================

1:网络编程(理解)

(1)网络编程:用Java语言实现计算机间数据的信息传递和资源共享
(2)网络编程模型
(3)网络编程的三要素
A:IP地址
a:点分十进制
b:IP地址的组成
c:IP地址的分类
d:dos命令
e:InetAddress

* InetAddress的成员方法:
* public static InetAddress getByName(String host):根据主机名或者IP地址的字符串表示得到IP地址对象
* public String getHostName(): 获取主机名
* public String getHostAddress(): 获取IP地址
/**
		 * public static InetAddress getByName(String host)
		 * 根据主机名或者IP地址的字符串表示得到IP地址对象
		 */
		// InetAddress address = InetAddress.getByName("liuyi");
		// InetAddress address = InetAddress.getByName("192.168.12.92");
		InetAddress address = InetAddress.getByName("192.168.12.54");

		/**
		 * 获取主机名
		 */
		String name = address.getHostName();

		/**
		 * 获取IP地址
		 */
		String ip = address.getHostAddress();

		System.out.println(name + "---" + ip);

B:端口
是应用程序的标识。范围:0-65535。其中0-1024不建议使用。
C:协议
UDP:数据打包,有限制,不连接,效率高,不可靠

/*
 * UDP协议发送数据:
 * A:创建发送端Socket对象:new DatagramSocket()
 * B:创建数据,并把数据打包:new DatagramPacket(bys, length, address, port);
 * C:调用Socket对象的发送方法发送数据包
 * D:释放资源
 */
public class SendDemo {
	public static void main(String[] args) throws IOException {
		// 创建发送端Socket对象:DatagramSocket()
		DatagramSocket ds = new DatagramSocket();

		// 创建数据,并把数据打包 DatagramPacket(byte[] buf, int length, InetAddress address, int port)
		byte[] bys = "hello,udp,我来了".getBytes();
		// 长度
		int length = bys.length;
		// IP地址对象
		InetAddress address = InetAddress.getByName("10.104.7.122");
		// 端口
		int port = 10086;
		DatagramPacket dp = new DatagramPacket(bys, length, address, port);

		// 调用Socket对象的发送方法发送数据包 public void send(DatagramPacket p)
		ds.send(dp);

		// 释放资源
		ds.close();
	}
}
/*
 * UDP协议接收数据:
 * A:创建接收端Socket对象:DatagramSocket(int port)
 * B:创建一个数据包(接收容器)
 * C:调用Socket对象的接收方法接收数据
 * D:解析数据包,并显示在控制台
 * E:释放资源
 */
public class ReceiveDemo {
	public static void main(String[] args) throws IOException {
		// 创建接收端Socket对象:DatagramSocket(int port)
		DatagramSocket ds = new DatagramSocket(10086);

		// 创建一个数据包(接收容器):DatagramPacket(byte[] buf, int length)
		byte[] bys = new byte[1024];
		int length = bys.length;
		DatagramPacket dp = new DatagramPacket(bys, length);

		// 调用Socket对象的接收方法接收数据:public void receive(DatagramPacket p)
		ds.receive(dp); // 阻塞式

		// 解析数据包,并显示在控制台
		// 获取对方的ip
		// public InetAddress getAddress()
		InetAddress address = dp.getAddress();
		String ip = address.getHostAddress();
		// public byte[] getData():获取数据缓冲区
		// public int getLength():获取数据的实际长度
		byte[] bys2 = dp.getData();
		int len = dp.getLength();
		String s = new String(bys2, 0, len);
		System.out.println(ip + "传递的数据是:" + s);

		// 释放资源
		ds.close();
	}
}


TCP:建立数据通道,无限制,效率低,可靠
(3)Socket机制
A:通信两端都应该有Socket对象
B:所有的通信都是通过Socket间的IO进行操作的
(4)UDP协议发送和接收数据(掌握 自己补齐代码)
发送:
创建UDP发送端的Socket对象
创建数据并把数据打包
发送数据
释放资源

接收:
创建UDP接收端的Socket对象
创建数据包用于接收数据
接收数据
解析数据包
释放资源
(5)TCP协议发送和接收数据(掌握 自己补齐代码)
发送:
创建TCP客户端的Socket对象
获取输出流,写数据
释放资源

接收:
创建TCP服务器端的Socket对象
监听客户端连接
获取输入流,读取数据
释放资源
(6)案例:
A:UDP
a:最基本的UDP协议发送和接收数据
b:把发送数据改进为键盘录入
c:一个简易聊天小程序并用多线程改进
B:TCP
a:最基本的TCP协议发送和接收数据
b:服务器给出反馈
c:客户端键盘录入服务器控制台输出
d:客户端键盘录入服务器写到文本文件
e:客户端读取文本文件服务器控制台输出
f:客户端读取文本文件服务器写到文本文件
g:上传图片
h:多线程改进上传文件

============================================================================

1:反射(理解)

(1)类的加载及类加载器
(2)反射:
通过字节码文件对象,去使用成员变量,构造方法,成员方法
(3)反射的使用
A:通过反射获取构造方法并使用
B:通过反射获取成员变量并使用
C:通过反射获取成员方法并使用
(4)反射案例
A:通过反射运行配置文件的内容
B:通过反射越过泛型检查
C:通过反射给任意的一个对象的任意的属性赋值为指定的值
(5)动态代理

2:设计模式

(1)装饰设计模式
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

Scanner sc = new Scanner(System.in);
(2)模版设计模式

3:JDK新特性

(1)JDK5(掌握)
装箱和拆箱
泛型
增强for
静态导入
可变参数
枚举
(2)JDK6(了解)
(3)JDK7(理解)
二进制的表现形式
用_分隔数据
switch语句可是用字符串
泛型推断(菱形泛型)
多catch的使用
自动释放资源的用法
(4)JDK8(了解)
可以去网上了解资料

 

 

 

 

 

 

 

 

 

 

 

  • 14
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值