Java基础
- 第1章Java语言概述
- 第2章基本语法上(变量与运算符)
- 第3章基本语法下
- 第4章面向对象上
- 第5章 数组
- 第6章 面向对象下
- 第7章 高级类特性
- 第8章 Java常用类
- 第10章 集合
- 第12章 IO流
- 第13章 多线程
- 第15章 网络编程
第1章Java语言概述
1.1 基础常识
软件:应用软件和系统软件
和用户的交互方式:图形化界面 vs 命令行
计算机硬件介绍:
Dos命令:
1.2 Java语言版本迭代概述
Sun公司1995年发布了Jdk1.0
Jdk1.5以后更命为5.0
JavaSE : 核心类库 + 图形化界面开发的API
JavaEE : 企业级开发(后台开发)
JavaME:手机开发,但是已经被Android所替代
JavaCard :一些小内存的设备上使用
1.3 Java语言应用的领域
大数据,后台开发,Android , 智能家电,车载应用
1.4 Java语言的特点
面向对象:封装性,继承性,多态性
健壮性 : ①去掉了C和c++的指针 ②增加了垃圾回收机制(GC)
跨平台性:一次编译到处运行。依赖于JVM
1.5 开发环境的搭建
1.5.1 JDK、JRE、JVM的关系
1.5.2 JDK的下载、安装
1.5.3 环境变量的配置
为什么要配置环境变量?
为了在任何目录下都可以访问得到Java开发工具集
如何配置?
1.6 开发体验——HelloWorld
1.6.1编写
创建一个以.java结尾的文件,该文件叫做源文件
1.6.2编译:
(Javac 源文件名.java ) 进行编译可以生成一个或多个字节码文件。
1.6.3 运行:
(Java 字节码文件名 ) 运行该程序
1.7 常见问题的解决
1.8 注释
单行注释: //
多行注释: /* /
文档注释:/* */ (java特有的)
注意:
①多行注释不能嵌套使用
②不会将注释编译到字节码文件中
说明:
①用来对方法类,或者某些语句进行说明。
②可以用来调试代码。
③文档注释可以被javadoc所解析生成文档说明
1.9总结第一个程序
①.一个源文件中可以有多个类,多个类的类名不能相同,只能有一个类被public所修饰且类名和源文件名需要保持一致。
②.java严格区分大小写。
③.每行代码写完以后要以";"结尾。
④.以.java结尾的文件叫做源文件。以.class结尾的文件叫做字节码文件。
1.10Java API 文档(Application Programming Interface)
类似于新华字典。Orcal公司编写的一套关于java的说明文档
1.11良好的编程风格
第2章基本语法上(变量与运算符)
2.1关键字与标识符
2.1.1 java关键字的使用
关键字:
定义:被java赋予了特殊的含义的字符串(单词)
特点:所有字母全部是小写
2.1.2 保留字
Java现版本暂未使用,将来的版本中可能会作为关键字使用
2.1.3 标识符的使用
标识符:凡是需要自己命名的地方都叫做标识符
2.1.4 标识符的规则与规范
定义合法标识符规则:
由26个英文字母大小写,0-9 ,_或 $ 组成
数字不可以开头。
不可以使用关键字和保留字,但能包含关键字和保留字。
Java中严格区分大小写,长度无限制。
标识符不能包含空格。
Java中的名称命名规范:
包名:多单词组成时所有字母都小写:xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
注意:要做到见名知义
2.2 变量的使用
2.2.1按数据类型分类
基本数据类型 vs 引用数据类型(接口,类,数组)
基本数据类型:
byte(1字节 1字节 = 8位 -128 ~ 127)
short(2字节)
int(4字节)
long(8字节)
float(4字节)
double(8字节)
char(2字节)
boolean
2.2.2定义变量的格式:
格式: 变量的类型 变量名 = 变量值;
例:
int a = 10;
int b;
b = 10;
int aa,bb,cc;
aa = bb = cc = 10;
2.2.3变量使用的注意点:
1.变量应该先声明再使用
2.同一作用域内的变量名不能相同
3.变量的作用域就是在声明它的那对大括号内
2.3变量间的运算
2.3.1基本数据类型间运算
一 基本数据类型之间的运算(7种 不包括(boolean))
1.自动类型提升:
小容量的变量和大容量的变量做运算结果用大容量的类型来接收。
byte,short,char -> int -> long -> float -> double
2.强制类型转换:自动类型提升的逆过程
注意:容量指的是变量的值的范围
2.3.2 String与基本数据类型(8种)间运算
字符串的赋值方式:
String s = “xiaoloongge”;
String str = new String(“xiaoloongge”);
说明:
1.String与基本数据类型只能做连接运算
2.String与基本数据类型运算结果还是String
2.4运算符
2.4.1算术运算符:
+(正) -(负) + - * / % 前++ 后++ 前-- 后-- +(连接符)
代码:
int n = -5;
System.out.println(n);
//除法
int a = 12;
int b = 5;
int c = a / b; //2
//需求 :得到浮点数
double d = a / b; //2.0
d = a / (b + 0.0); //2.4
d = (a + 0.0) / b; // 2.4
d = a * 1.0 / b; //2.4
d = 12 / 5 * 5; //10.0
int dd = 12 * 5 / 5; //12 建议:再做算术运算的时候如果即有乘法又有除法,建议先做乘法运算
System.out.println(dd);
//取模 用法:1.判断是否可以整除
System.out.println(1 % 2);
System.out.println(2 % 2);
System.out.println(3 % 2);
System.out.println(4 % 2);
System.out.println(5 % 2);
System.out.println(6 % 2);
System.out.println(7 % 2);
System.out.println(8 % 2);
System.out.println(9 % 2);
System.out.println("-------------------------");
//取模结果的正负和被模数有关,被模数是正的结果就是正的,反之就是负的。
System.out.println(-1 % 2); // -1
System.out.println( 1 % -2); // 1
System.out.println( -1 % -2); // -1
System.out.println("--------------------------");
//后++ 先赋值再自身加1
int aa = 10;
int bb = aa++;
System.out.println("aa=" + aa + " bb=" + bb); //aa=11 bb=10
//前++ 先自身加1再赋值
int aa2 = 10;
int bb2 = ++aa2;
System.out.println("aa2=" + aa2 + " bb2=" + bb2); //aa2=11 bb2=11
//后-- 先赋值再自身减1
int aa3 = 10;
int bb3 = aa3--;
System.out.println("aa3=" + aa3 + " bb3=" + bb3); //aa3=9 bb3=10
//前-- 先自身减1再赋值
int aa4 = 10;
int bb4 = --aa4;
System.out.println("aa4=" + aa4 + " bb4=" + bb4); //aa4=9 bb4=9
System.out.println("-----------------------------");
int number1 = 12;
int number2 = 5;
double dou = number1 / (double)number2;
System.out.println(dou);
2.4.2赋值运算符:
赋值运算符: = +=, -=, *=, /=, %=
①编译不通过,因为short进行运算时会先提升为int类型
②编译通过,不会改变原来的数据类型。
注意:
赋值运算有强制转换功能,可以避免类型提升.会有溢出风险
如:
byte a = 127;
a += 1; //相当于a = (byte)(a + 1)
System.out.println(a);//a = -128
2.4.3比较运算符:
特点:结果为boolean类型
2.4.4逻辑运算符:
特点:
1.逻辑运算符 运算的是boolean类型
2.逻辑运算符的结果为boolean类型
总结:
&&和& :符号两边的表达式的结果都为true的时候运算符结果为true
|和|| : 符号两边的表达式的结果都为false的时候运算符结果为false
! : 取反
^ : 相同为假,不同为真。
[面试题] & 和 && 的区别?| 和 ||的区别?
& 和 &&的区别? – 两边都为true结果才为true(左边为false结果一定为false)
左边的式子的结果为true & 和 && 右边的式子都会执行。
左边的式子的结果为false
& 右边的式子仍然会执行
&& 右边的式子不再执行
| 和 || 的区别? – 两边都为false结果才为false(左边为true结果一定为true)
左边的式子的结果为false | 和 || 右边的式子都会执行。
左边的式子的结果为true
| 右边的式子仍然会执行
|| 右边的式子不再执行
2.4.5位运算符:
<< : 左移 : 在一定范围内每向左移一位原来的数乘以2
>> : 右移 : 在一定范围内每向右移一位原来的数除以2
>>> : 无符号右移
>>和>>>的区别?
>> 如果是正数最高位用0补,如果是负数最高位用1补
>>> 无论是正数还是负数最高位都用0补
2.4.6三元运算符:
格式 : (条件表达式)?表达式1 :表达式2;
说明 :
1.条件表达式的结果只能为boolean。如果条件表达式结果为true那么结果为表达式1,反之结果为表达式2.
2.三元运算符可以嵌套使用但是不建议
3.表达式1和表达式2的类型必须一致 (如果可以自动类型提升那么就可以)
不行的案例 : 表达式1为int 表达式2为String
可行的案例 : 表达式1为int 表达式2为double 结果用double接收
4.三元运算符一定可以使用if-else来替换,反之不成立。两者都可以的时候建议使用三元运算符,因为三元运算符的效率略高一些。
代码:
//需求:求两个变量的最大值
int m = 10;
int n = 10;
int maxNumber = (m > n) ? m : n;
String str = (m > n) ? "m" : "n"; //条件不满足是 m 小于等于 n 结果都为false
System.out.println("maxNumber = " + maxNumber);
System.out.println("str = " + str);
//需求:求三个数的最大值
int a = 10;
int b = 20;
int c = 30;
//int max = (a > b)? a : b;
//max = (max > c)? max : c;
第3章基本语法下
3.1分支结构
3.1.1 if-else结构
格式1:
if(条件表达式){
执行语句;
}
格式2:二选一
if(条件表达式){
执行语句1;
}else{
执行语句2;
}
格式3:多选一
if(条件表达式1){
执行语句1;
}else if(条件表达式2){
执行语句2;
}
…
else{
执行语句n;
}
说明:
1.条件表达式的结果必须为boolean
2.else可以省略
3.如果执行语句只能一行代码,则可以省略大括号。(不建议)
4.多个条件表达式的关系如果为包含关系,范围小的在上边,范围大的在下边。
如果是互斥关系,那么谁上谁下都可以。
代码:
//if
boolean handsome = false;
if(handsome){
System.out.println("我们结婚吧!!!");
}
System.out.println("-------------------------");
//if - else
boolean hasMoney = false;
if(hasMoney){
System.out.println("今晚我是你的人");
}else{
System.out.println("我不是那种人");
}
System.out.println("-------------------------");
// if - else if - else
int age = 100;
if(age < 18){
System.out.println("就不要看片了");
}else if(age < 30){
System.out.println("还是不要看片了没事多陪陪媳妇吧");
}else if(age < 50){
System.out.println("年轻看片多了,年纪大了就多运动吧");
}else{
System.out.println("还是在家里睡觉吧");
}
System.out.println("程序执行完毕");
3.1.2 switch-case结构
格式:
switch(表达式){
case 常量1:
执行语句1;
break;
case 常量1:
执行语句1;
break;
case 常量1:
执行语句1;
break;
......
default:
执行语句N;
break;
}
说明:
1.根据switch中的表达式的值依次进行case匹配,一旦匹配成功,则执行该case中的执行语句。如果在执行过程中遇到break则跳出switch-case结构。如果没有break继续向下执行,执行到switch-case结构中最后的语句,再跳出switch-case结构。如果没有匹配成功则执行defult中的代码。
2.break是可选的。break用来跳出switch-case结构。
3.default的位置是灵活的。
4.表达式的类型只能是 byte short int char 枚举 String
5.case后面只能跟常量 (1,2,100 ,‘a’ ,“aaa”) 且所有case子句中的值应是不同的
3.2 循环结构
四个条件:
①初始化条件
②循环条件
③循环体
④迭代条件
3.2.1 for循环
格式:
for(初始化条件; 循环条件;迭代条件){
循环体;
}
执行顺序: 1 – 2 – 3 – 4 – 2 – 3 – 4 … 2
代码:
int sum = 0; //用来统计偶数的个数
int sumNumber = 0; //用来统计所有偶数的和
for(int i = 1; i <= 100; i++){
//判断是否是偶数
if(i % 2 == 0){
sum++;
sumNumber += i; // sumNumber = sumNumber + i;
System.out.println(i);
}
}
3.2.2 while循环
格式:
初始化条件;
While(循环条件){
循环体;
迭代条件;
}
注意:迭代条件一定要写否则就成了死循环
代码:
//初始化条件
int i = 1;
int number = 0; //偶数的个数
int sum = 0; //偶数的总和
while(i <= 100){ //循环条件
//循环体
if(i % 2 == 0){
number++;
sum += i;
System.out.println(i);
}
i++; //迭代条件 - 如果没有迭代条件就会变成一个死循环
}
3.2.3 do-while循环
格式:
初始化条件:
do{
循环体;
迭代条件;
}while(循环条件);
[面试题] while和do-while的区别?
while可能一次循环体也不执行。do-while至少执行一次循环体.
代码:
int i = 1;
int number = 0; //偶数的个数
int sum = 0; //偶数的总和
do{
if(i % 2 == 0){
number++;
sum += i;
System.out.println(i);
}
i++;
}while(i <= 100);
##3.3 死循环
格式 :
for(;😉{}
while(true){}
do{}while(true);
如何终止循环:
①使用break关键字
②将循环条件的结果改为false
代码:
/*
for(;;){
System.out.println("小泽我爱你");
}
*/
/*
while(true){
System.out.println("我爱你小泷");
}
*/
//退出死循环的方式
boolean boo = true;
while(boo){
System.out.println("我爱你小泷");
boo = false;
}
//break 用来结束当前循环
while(true){
System.out.println("我爱你小泷");
break; //用来结束当前循环
}
3.4嵌套循环
嵌套循环 :一个循环a中还有一个循环b a循环叫做外层循环b循环叫内层循环
执行的次数 = 外层循环的次数 * 内层循环的次数
说明 :
①外层循环一次内层循环一轮。
② 外层循环控制行,内层循环控制列。
代码:
//需求:每次只能输出一个* 输出 *****
for(int i = 0; i < 5; i++){
System.out.print("*");
}
System.out.println("------------------------------");
/*
需求:输出 图形
*****
*****
*****
*****
*****
*/
for(int i = 0; i < 5; i++){
for(int j = 0; j < 5; j++){
System.out.print("*");
}
System.out.println();
}
/*
需求:
i j
* 1 1
** 2 2
*** 3 3
**** 4 4
***** 5 5
*/
for(int i = 1; i <= 5; i++){
for(int j = 1; j <= i; j++){
System.out.print("*");
}
System.out.println();
}
/*
需求:
i j
***** 1 5
**** 2 4
*** 3 3
** 4 2
* 5 1
*/
for(int i = 1; i <= 5; i++){
for(int j = 1; j <= 6 - i; j ++){
System.out.print("*");
}
System.out.println();
}
System.out.println("----------------------------------");
/*
需求:
i j k
----* 1 4 1
---* * 2 3 2
--* * * 3 2 3
-* * * * 4 1 4
* * * * * 5 0 5
*/
for(int i = 1; i <= 5; i++){
for(int j = 1; j <= 5 - i; j++){
System.out.print(" ");
}
for(int k = 1; k <= i; k++){
System.out.print("* ");
}
System.out.println();
}
3.5break和continue的使用
break: 1.用在switch-case中用于结束switch-case结构.
2.用在循环语句中用于结束当前(本层)循环.
3.在嵌套循环中-结束的是包含它的那个循环的本层循环
continue:
1.只能在循环语句中使用。用于结束当次循环。
2.在嵌套循环中-结束的是包含它的那个循环的当次循环
标签 :
l : for(int i = 1; i <= 5; i++){
for(int j = 1; j <= 5; j++){
if(j == 2){
break l; //结束含有标签的那层循环
//编译错误 break continue后面不能直接再跟语句因为根本不会执行到
//System.out.println("aaaaaaaaaaaaaaaaa");
}
System.out.println("j=" + j);
}
System.out.println("i=" + i);
}
第4章面向对象上
4.1类与对象
4.1.1 面向对象学习的三条主线:
1.java类及类的成员
2.面向对象的三大特征
3.其它关键字
4.1.2面向对象与面向过程(理解)
面向过程:面向过程,强调的是功能行为
面向对象:将功能封装进对象,强调具备了功能的对象
4.1.3面向对象中两个重要的概念:
类:对一类事物的描述,是抽像的概念上的定义
对象:具体的实实在在的个体。(对象是由类派生出来的。new出来的)
4.1.4 面向对象思想落地实现的规则一
1.创建一个java类
2.在类中提供必要的属性和方法
3.通过类创建对象。 Person p = new Person();
4.调用对象中的属性和方法 。对象名.方法名 、 对象名.属性名
4.1.5对象的创建与对象的内存解析
4.1.6.匿名对象:
匿名对象:我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象
使用场景:常常作为实参进行传递。
例:
new Person();
4.2类的结构之一:属性
4.2.1 变量的分类
安照数据类型 :基本数据类型 vs 引用数据类型
按照位置来分 : 成员变量 vs 局部变量
4.2.2 局部变量和成员变量的相同和不同
相同点:
1.都是先声明后使用
2.都有作用域
3.声明的格式 : 变量的类型 变量名 = 变量值
不同点:
1.位置不同
*成员变量 :在类中,方法构造器等结构外。
- 局部变量 :在构造器和方法等结构中声明的变量,构造器和方法的形参。
2.默认值不同:
成员变量:
基本数据类型
byte short int long -> 0
float double -> 0.0
char -> \u0000
boolean -> false
引用数据类型 : null
局部变量:没有默认值
3.内存的位置不同 - 局部变量:在栈中
- 成员变量 : 在堆中
4.权限修饰符不同: - 局部变量:没有权限修饰符
- 成员变量 :可以使用四种权限修饰符 。
private 缺省的 protected public
4.2类的结构之一:方法
4.2.1 方法
方法的格式:
权限修饰符 返回值类型 变量名(形参列表){
方法体;
}
方法的说明:
1.权限修饰符 : private 缺省的 protected public
2.返回值类型 :只有两种 - void(没有返回值) / 具体的类型(基本数据类型和引用数据类型)具体的类型 : 那么方法的最后必须是 “return 具体的类型”.
3.方法名 : 遵守标识符的规则和规范。要做到见名知义。
4.形参列表 :可以是0个 1个或多个 。多个之间用","隔开.
(变量类型 变量名,变量类型 变量名 …)
形参属于局部变量。
5.方法体:方法的具体功能的实现。只能调用方法的时候才会执行方法体。
return关键字:
1.在方法中使用
2.如果方法没有返回值,那么”return”用来结束当前的方法
3.如果方法有返回值,”return 具体的数据”用来返回数据并结束当前方法
4.2.2 方法的重载
方法的重载的概念:
同一个类中相同的方法名不同的形参列表构成重载
举例:
// 需求:计算两个数(int类型)的和
public void add(int a, int b) {
System.out.println("int int int");
System.out.println(a + b);
}
// 需求 :计算两个数(double类型)的和
public void add(double a, double b) {
System.out.println(a + b);
}
public void add(int a, double b) {
}
//构成重载
public void add(double b,int a){
}
如何确定类中某一个方法的调用:方法名 + 形参列表
说明:
1.方法的重载和权限修饰符,返回值类型,形参列表变量名都没有关系
2.形参列表不同指的是形参的类型和个数
4.2.3 值传递
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
总结:
①基本数据类型: 传递的是变量中具体的数值
②引用数据类型: 传递的是对象的地址值
图示:
4.3面向对象的特征一:封装与隐藏
4.3.1 为什么要使用封装性?
当我们给对象中的属性进行赋值时,只能对属性的类型和数值范围进行限制。但是在实际中往往我们还会有其它的限制条件。那么这些限制条件不能在属性的声明处加以限制。所以我们采用以下方式。
1.将属性私有化,不让对象可以直接调用属性并给属性赋值。
2.提供公共的方法,可以通过方法给属性进行赋值,并在方法中可以加以其它的限制条件。
4.3.2 封装性的体现
封装性思想具体的代码体现:
class Person{
private int age;
public void setAge(int age){
if (age < 0 || age > 120) {
System.out.println("非法操作");
return;
}
this.age = age;
}
public int getAge(){
return age;
}
}
封装性的体现(狭义上)
1.将属性私有化
2.提供公共的set/get方法
封装性的体现(广义上)
1.可以修饰属性的四种权限修饰符 - private 缺省的 protected public
2.类的成员可以使用四种权限修饰符 - 属性,方法,构造器,内部类
3.类只能被public和缺省的所修饰
4.3 类的成员之:构造器
作用:1.创建对象 2.给对象进行初始化
格式:
权限修饰符 类名(形参列表){初始化语句;}
说明:
1.在一个类中如果没有显示的声明构造器,那么系统创建对象的时候会默认为我们提供一个空参的构造器。
2.如果在类中显示的声明了构造器,那么系统就不会再为我们提供空参的构造器了
3.一个类中可以有多个构造器,多个构造器之间构成重载
总结:创建对象必调构造器
4.3 属性的赋值顺序
赋值方式 : 1.默认值 2. 显示赋值 3. 对象.属性、对象.方法 4.构造器赋值
赋值顺序 :1 – 2 – 4 - 3
4.3 关键字:this
this关键字表示:当前对象
this可以用来调用 :属性,方法,构造器
this调用属性和方法:
在方法和构造器中,调用属性时往往我们会省略掉"this."。但是如果方法和构造器中的形参的名字和属性的名字一致时,我们必须使用 this.属性名 来和 形参加以区分。
this调用构造器 :
1.格式:this(形参列表) - 用来调用本类中的其它构造器
2.this(形参列表) 必须放在构造器的首行(一个构造器中最多只能有一个this(形参列表))
3.如果一个类中的构造器有n个 ,那么最多只能有n-1个this(形参列表)
4.3 关键字:import和package
- 关键字 package :
1.java为了对所有的类进行统一管理,所以有了包的概念
2.不同包中的类的类名可以相同
3.每一个"."代表一层目录
4.包名 : com.公司名.项目名.模块名
5.package必须声明在源文件的首行 - 关键字import
1.在源文件中使用import显式的导入指定包下的类或接口
2.声明在包的声明和类的声明之间。
3.如果需要导入多个类或接口,那么就并列显式多个import语句即可
4.举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
5.如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
6.如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
7.import static组合的使用:调用指定类或接口下的静态的属性或方法
8.如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
第5章 数组
数组是多个相同类型数据的组合,实现对这些数据的统一管理;
数组中的元素可以是任何数据类型,包括基本类型和引用类型;
数组属引用类型,数组型数据是对象(object),数组中的每个元素相当于该对象的成员变量;
5.1 一维数组
5.1.1 数组的声明和初始化
声明方式:
String[] names;
int numbers[]; //不建议使用
静态初始化:
String[] names = new String[]{“aa”,”bb”}
int[] numbers = {1,2,3,4} //声明和赋值不能分开写
动态初始化:
String[] names = new String[5];
说明:无论是静态初始化还是动态初始化一旦数组创建成功
那么数组的长度不可变。
5.1.2 调用数组中的元素
String[] names = new String[5];
- 数组的索引(下角标)是从0开始的.
- 取值
String name = names[0]; - 赋值
names[0] = “cc”;
5.1.3 数组的属性 – length (数组的长度)
int len = names.length
5.1.4 数组的遍历
for(int i = 0; i < names.length; i++){
System.out.println(names[i]);
}
5.1.5 数组元素的默认值
byte short int long - 0
float double – 0.0
char - \u0000
boolean – false
引用数据类型 :数组,类,接口 - null
5.1.6 数组的内存分析
5.2 二维数组
5.2.1数组的声明和初始化
第一个[]指的是二维数组中的第几个元素
第二个[] 指的是该数组元素的第几个元素
数组的声明:
String[][] persons;
String[] persons[]; //不建议使用
String persons[][];//不建议使用
静态初始化:
String[][] persons = new String[][]{{“小泽”,“110”},{“小苍”,120}}
String[][] persons = {{“小泽”,“110”},{“小苍”,120}}
动态初始化:
//二维数组的长度是2,每个元素的长度还是2
String[][] persons = new String[2][2];
String[][] persons = new String[2][];
persons[0] = new String[3];
persons[1] = new String[2];
5.2.1调用数组中的元素
String[][] persons = {{“张三”,“18”},{“李四”,“20”}};
//0指的是二维数组中的第几个元素
//1 指的是该数组元素的第几个元素
System.out.println(persons[0][1]); //18
5.2.1数组的属性
String[][] persons = {{“张三”,“18”},{“李四”,“20”}};
二维数组的长度 : persons.length
二维数组元素的长度 : persons[0].length
5.2.1数组的遍历
for(int i = 0; i < persons.length; i++){
for(int j = 0; j < persons[i].length; j++){
System.out.println(persons[i][j]);
}
}
5.2.1二维数组元素的默认值
二维数组元素的默认值是 : null
二维数组的元素是一维数组,数组是引用数据类型所以默认值是null
5.2.1二维数组的内存分析
5.3数组常见算法
/*
* 遍历
*/
public void listArray(int[] numbers) {
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
}
/*
* 求数组中的最大值
*/
public int getMaxNumber(int[] numbers) {
// 求数组元素的最大值
int max = numbers[0]; // 不要写0
for (int i = 1; i < numbers.length; i++) {
if (numbers[i] > max) {
max = numbers[i];
}
}
return max;
}
/*
* 求数组元素的最小值
*/
public int getMinNumber(int[] numbers) {
// 求数组元素的最小值
int min = numbers[0];
for (int i = 1; i < numbers.length; i++) {
if (numbers[i] < min) {
min = numbers[i];
}
}
return min;
}
/*
* 求数组元素的和
*/
public int sum(int[] numbers) {
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
/*
* 求平均数
*/
public int average(int[] numbers) {
int sum = sum(numbers);
return sum / numbers.length;
}
/*
* 数组的复制
*/
public int[] copy(int[] numbers) {
// 数组的复制
int[] nCopy = new int[numbers.length]; // 创建一个新的数组
for (int i = 0; i < numbers.length; i++) {
nCopy[i] = numbers[i];
}
return nCopy;
}
/*
* 数组的反转
*/
public void reverse(int[] numbers) {
for (int i = 0, j = numbers.length - 1; i < numbers.length / 2; i++, j--) {
int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
}
/*
* 线性查找 : 如果没有找到返回-1
*
* numbers : 数据源 findNumber : 需要查找的数据
*/
public int findNumber(int[] numbers, int findNumber) {
int index = -1; // 查找到元素所在的索引值
for (int i = 0; i < numbers.length; i++) {
if (findNumber == numbers[i]) {
index = i;
}
}
return index;
}
public void bubbleSort(int[] n,String str) {
if ("asc".equals(str)) {
// 从小到大
for (int i = 0; i < n.length - 1; i++) {
for (int j = 0; j < n.length - 1 - i; j++) {
if (n[j] > n[j + 1]) {
int temp = n[j];
n[j] = n[j + 1];
n[j + 1] = temp;
}
}
}
} else if("desc".equals(str)) {
// 从大到小
for (int i = 0; i < n.length - 1; i++) {
for (int j = 0; j < n.length - 1 - i; j++) {
if (n[j] < n[j + 1]) {
int temp = n[j];
n[j] = n[j + 1];
n[j + 1] = temp;
}
}
}
}
}
5.4 Arrays工具类的使用
5.5数组常见异常
第一种 : 下角标越界
第二种 : 空指针异常
代码:
int[] numbers = new int[2];
//下面的两个方式都会发生下角标越界
// System.out.println(numbers[2]);
// System.out.println(numbers[-2]);
//空指针异常
// int[] n = null;
// n[0] = 5;
//空指针异常
/*
String[] s = new String[2];
String str = s[0]; //因为 s[0]元素是个null
System.out.println(str.toLowerCase()); 相当于 null.toLowerCase();
*/
//空指针异常
/*
String[][] s = new String[2][];
s[0][0] = "aa";
*/
5.6可变个数形参
格式:方法名(变量的类型 … 变量名)
说明:
1.可变形参的个数可以是0个 1个 或者多个
2.形参列表如果是相同类型的可变形参和数组那么不构成重载。 (int … args)可以理解成int[] args
3.在形参列表中 可变形参只能放在最后一个
4.形参列表中只能有一个可变形参
第6章 面向对象下
6.1 面向对象特性之 : 继承性
6.1.1 为什么要有类的继承性?(继承性的好处)
①减少了代码的冗余,提高了代码的复用性;
②更好的扩展性
③为多态性的使用提供了前提
6.1.2 继承的格式:
A extends B
A 子类 (SubClass)
B 父类(SuperClass) 超类,基类
6.1.2子类继承父类以后有哪些不同?
子类继承父类以后,就拥有了父类中的属性和方法.
当父类中的属性被私有化后,子类就不能直接调用了。但我们还是认为子类继承到了父类中的被私有化的属性。我们可以通过间接的方式进行调用。
6.1.3 java中继承性的说明
1.子类除了可以继承父类中的属性和方法外,还可以自己再定义其它的属性和方法
2.一个子类只可以有一个父类,一个父类可以有多个子类。(java的类是单继承的)
3.子类和父类的概念是相对而言的。如果A类的父类是 B类,B类的父类是C类。 B类是A类的直接父类,C类是A类的间接父类。
4.子类不仅可以继承直接父类的属性和方法,还可以继承间接父类中的属性和方法。
5.我们不认为类中的构造器可以被继承
6.2 方法的重写
6.2.1什么是方法的重写 :
定义 :子类可以对父类中同名同参的方法进行覆盖(覆写)
6.2.2方法重写后的调用 :
当子类重写了父类的方法后。通过子类对象调用父类的方法,实际上调用的是子类重写父类后的方法。
6.2.3 说明
权限修饰符 返回值类型 方法名(形参列表){
方法体;
}
子类重写的方法 父类被重写的方法
1.子类重写的方法和父类被重写的方法,方法名和形参列表必须一致。
2.子类重写的方法的权限修饰符 不小于 父类被重写方法的权限有修饰符
3.子类重写的方法的返回值类型 不大于 父类被重写方法的返回值类型
(子类重写的方法的返回值类型和父类被重写的方法的返回值类型 如果不是void
那么需要有子父类关系(如果是相同的类型也可以))
①父类被重写方法的返回值类型是void那么子类重写的方法的返回值类型必须是void
②父类被重写方法的返回值类型是 int 子类重写的方法的返回值类型是double(不可以,没有子父类关系)
③ A类继承B类,B类继承C类。父类被重写的方法返回值类型是B类 那么子类重写的方法的返回值类型只能是B类或A类
4.子类方法抛出的异常不能大于父类被重写方法的异常
6.2.4 注意
1.父类中的被私有化的方法,我们不认为子类可以重写
2.子类和父类同名同参的方法,要么同时加static,要么同时不加static.如果加上static那么子类和父类同名同参的方法不是重写的方法.
6.3 关键字super
super可以调用:属性,方法,构造器
super调用属性和方法:
当有了继承以后。我们就可以在方法和构造器中,使用"super.“调用父类中的方法和属性.往往我们都会省略掉"super.”.但是如果子类重写了父类以后,或者子类中的属性名和父类中的属性名相同时。那么我们就必须显示的调用“super.”来指明调用的是父类中的属性和方法
super调用构造器:
格式 :super(形参列表)
说明 :
1.格式:super(形参列表) 用来调用父类中的指定的构造器
2.super(形参列表)必须放在构造器的首行
3.一个构造器中只能有一个super(形参列表)
4.一个构造器中super(形参列表),this(形参列表)只能有一个
5.如果子类中的构造器中没有显示的调用this(形参列表)和super(形参列表)那么默认调用的是super();
6.4 子类对象实例化过程(理解)
图示 :
过程上:子类通过继承父类,就可以获取父类中的属性和方法。那么通过子类对象就可以调用父类中的属性和方法
结果上:子类继承父类以后。子类一定会调用到 直接父类,间接父类…Object中的空参构造器。那么所有的父类都会被JVM加载到内存中。然后子类就可以调用到这些父类中的属性和方法。
注意:虽然父类都会被JVM加载到内存中,但是从始之终我们认为只创建了子类一个对象
6.5 面向对象的特性之:多态性
6.5.1多态性的理解:
一类事物的多种形态
6.5.2广义上多态性的体现:
①方法的重写,重载
②子类对象的多态性
6.5.3狭义上多态性的体现:
①子类对象的多态性
6.5.4何为子类对象多态性:
父类的引用指向子类的对象
6.5.5多态性的应用:
虚拟方法调用:编译看左边,运行看右边
编译时:看的是父类(调用的是父类中的方法)
运行时:看的是子类(调用的方法是子类重写父类中的方法)
6.5.6多态性的说明:
思考?什么是多态性?
父类的指向子类的对象,编译看左边,运行看右边
思考?属性有没有多态性?
没有
思考?如果需要调用子类中的特有的属性和方法怎么办?
向下转型(注意使用instancof判断对象的类型)
6.5.7多态性使用的前提:
①要有继承性
②要有方法的重写
6.5.8.关于向上转型与向下转型:
向上转型: 多态
向下转型: 将父类的引用强制转换成子类的对象
图:
为什么要使用向下转型?
父类的引用指向子类的对象。编译看左边运行看右边。实际上子类中的属性和方法都被加载到了内存中。因为编译看左边不能调用子类中特有的属性和方法。那么只能通过向下转型的方式来进行调用子类中特有的属性和方法。
说明:
①向下转型可能会出现: 类型转换异常
②向下转型可以使用:
强制类型转换符 – (强转的类型)
③向下转型需要判断:instanceof
a instanceof A : a是否是A类型的实例
6.5.9.面试题:多态是编译时行为还是运行时行为?
运行时行为
6.6 Object
6.6.1Object类的说明
①Object是所有类的基类
②Object中有9个方法,一个空参的构造器
6.6.2 equals方法
一 Object中equals方法的 实现
public boolean equals(Object obj) {
return (this == obj);
}
二 像系统中的String ,Date 等类 都重写了equals方法 .
用来比较内容。
三 一般情况下我们调用equals方法,都是用来比较对象的内容。所以往往我们都会在自定义类中重写equals方法,用来比较属性的内容。
6.6.3 toString方法
一 Object中的toString方法
- public String toString() {
return getClass().getName() + “@”
+ Integer.toHexString(hashCode());
}
二 像系统中的String ,Date 等类 都重写了toString方法 .用来输出对象中属性的值。
三 一般情况下我们调用toString方法,都是用来输出对象的内容。所以往往我们都会在自定义类中
重写toString方法,用来输出属性的内容。
6.6.4 面试题?==和equals的区别?
== 比较的是基本数据类型的话,比较的是具体的数值
== 比较的是引用数据类型的话,比较的是地址值(两个地址是否指向同一块内存)
equals 如果没有重写equals方法那么调用的是Object中的equals比较的还是地址值如果重写了equals方法,那么就安照重写后的内容进行比较。(通常重写equals比较的都是内容)
==和equals的区别
最大的区别是一个是方法一个是运算符
==是一个比较运算符,基本数据类型比较的是值,引用数据类型比较的是地址值。
(比较地址值即是指是否为同一个对象的引用)
equals()是一个方法,只能比较引用数据类型。重写前比较的是地址值,重写后比一般是比较对象的属性。
6.7关键字Static
6.7.1可以用来修饰的结构
属性,方法,代码块,内部类
6.7.2 static修饰属性
1.同一个类的多个对象各自拥有一份实例变量,多个对象共同拥有一份类变量。
2.当其中的一个对象对类变量进行修改,那么其它的对象看到的是修改后的结果。
3.类变量是随着类的加载而加载,实例变量是随着对象的创建而加载。
-
类的加载早于对象的创建。
4.类的加载只加载一次。
5.如何调用类变量:对象名.类变量名 类名.类变量名
6.类变量在方法区的静态域中,实例变量在堆中。
6.7.3 static修饰方法
1.静态方法随着类的加载而加载
2.调用静态方法 : 对象名.静态方法名 类名.静态方法名
3.静态方法中不能调用非静态方法和实例变量。因为类的加载优先于对象的创建。非静态方法中可以调用静态方法和类变量。
4.静态方法中可以使用super和this吗?不能
6.7.4 内存解析
6.7.5 什么时候使用static修饰属性和方法
static修饰属性:
①多个对象共同拥有一份属性的时候
②声明为常量时
static修饰方法:
① 调用类变量的时候
②当定义成工具类中的方法的时候
6.8类的结构:代码块
1.代码块的作用:用来对类和对象进行初始化
2.分类:静态代码块 vs 非静态代码块
格式:
静态代码块:static{}
非静态代码块: {}
注意:代码块只能被static修饰
3.说明
静态代码块:
1.作用 : 用来对类进行初始化
2.静态代码只执行一次。随着类的加载而加载。
3.静态代码块优先于非静态代码块执行。
4.静态代码块不可以调用实例变量和非静态方法
5.多个静态代码块从上到下依次执行
非静态代码块:
1.作用:用来对对象进行初化
2.随着对象的创建而执行
3.非静态代码块优先于构造器的执行
4.可以调用类变量和静态方法
5.多个非静态代码块从上到下依次执行
6.9属性的赋值顺序
属性赋值的方式 :
1.默认值 2.显示赋值 3.代码块赋值 4构造器赋值 5.对象.方法名 、对象名.属性名
顺序 :1 – 2 、3 - 4 – 5
注意:2和3谁在上谁先赋值
6.10 关键字final
Final修饰类 : 不能被继承。例:String,StringBuffer
Final修饰属性 :一般作为常量。且只能赋值一次
Final修饰方法 : 不能被重写
Final修饰的属性的赋值方式 :
显示赋值,代码块赋值,构造器赋值
第7章 高级类特性
7.1 关键字:abstract
1.abstract可以修饰:类,方法
2.abstract修饰方法 : 抽像方法
abstract修饰类: 抽像类
abstract不可以和哪些关键字一起使用:
3.说明:
abstract修饰类:
-
1.抽像方法所在的类必须是抽像类
-
2.抽像类不可以被实例化
-
3.抽像类中不一定有抽像方法。但是抽像方法所在的类必须是抽像类
abstract修饰方法:
-
1.抽像方法没有方法体
-
2.非抽像子类继承抽像类必须重写抽像类中的所有抽像方法
-
直接抽像父类如果重写了间接抽像父类中的抽像方法。那么非抽像子类可以不用重写。
-
3.非抽像子类只能重写了抽像父类中的抽像方法才可以实例化。如果不想重写抽像父类中的抽像方法。那么该子类也可以声明成抽像类。
4.abstrcat不可以和哪些关键字一起使用
final private static
4:代码
abstract class GeomericObject{
/*
* 几何图形的面积
*/
public abstract double findArea(); //抽像方法
//如果想让子类必须重写父类中的某个方法,那么这个方法也可以设计成抽像方法
public abstract void setRadius();
}
/*
* 三角形
*
* 1.抽像类不可以被实例化
* 2.抽像子类继承抽像父类可以不重写抽像方法
*/
abstract class Triangle extends GeomericObject{
/*
* 思考?抽像类不能被实例化,那么抽像类中还有构造器吗?
* 有构造器,因为子类对象的实例化过程。
*/
@Override
public double findArea() {
// TODO Auto-generated method stub
return 0;
}
}
/*
* 非抽像子类必须重写抽像父类中的所有的抽像方法,
* 直接抽像父类如果重写了间接抽像父类中的抽像方法。那么非抽像子类可以不用重写。
*/
class A extends Triangle{
@Override
public void setRadius() {
// TODO Auto-generated method stub
}
}
7.2 模板方法的设计模式
/*
* 用来计算代码运行的时间
*/
abstract class ComputerCode {
public void code() {
// 开始的时间
long startTime = System.currentTimeMillis();
// 计算的代码
codeTime();
// 结束时间
long endTime = System.currentTimeMillis();
// 求时间差
System.out.println("time=" + (endTime - startTime));
}
public abstract void codeTime();
}
class A extends ComputerCode{
@Override
public void codeTime() {
for (int i = 1; i <= 1000; i++) {
if(i % 2 == 0){
System.out.println("aaa=" + i);
}
}
}
}
class B extends ComputerCode{
@Override
public void codeTime() {
for (int i = 1; i <= 1000; i++) {
if(i % 2 != 0){
System.out.println("bbb=" + i);
}
}
}
}
7.3 关键字:interface
一 接口的声明格式 :
权限修饰符 interface 接口名{
}
二 说明
-
1.类和接口是并列关系
-
2.格式 :权限修饰符 interface 接口名{}
-
3.接口中只能声明常量和抽像方法(jdk1.7之前(包含1.7))
-
4.类和接口之间的关系: 类 implements 接口A,接口B
-
实现的关系,一个类可以实现多个接口。
-
5.接口和接口之间的关系 :接口A extends 接口B ,接口C
-
继承的关系,而且是多继承
-
6.接口不可以被实例化,接口中没有构造器
-
7.接口和类之间的多态性
三 接口和类的多态性
public class InterfaceTest2 {
public static void main(String[] args) {
Computer computer = new Computer();
Mouse mouse = new Mouse();
computer.work(mouse);
System.out.println("-----------------");
//匿名对象
computer.work(new Printer());
System.out.println("-------------------");
//匿名实现类的对象
USB keyboard = new USB() {
@Override
public void start() {
System.out.println("键 盘开始工作了");
}
@Override
public void stop() {
System.out.println("键 盘停止工作了");
}
};
computer.work(keyboard);
//匿名实现类的匿名对象
computer.work(new USB() {
@Override
public void start() {
System.out.println("摄像头开始工作了");
}
@Override
public void stop() {
System.out.println("摄像头停止工作了");
}
});
}
}
class Computer{
public void work(USB usb){
usb.start();
System.out.println("-----------工作中-----------");
usb.stop();
}
}
interface USB{
void start();
void stop();
}
class Mouse implements USB{
@Override
public void start() {
System.out.println("------鼠标开始工作了--------");
}
@Override
public void stop() {
System.out.println("------鼠标停止工作了--------");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("------打印机开始工作了--------");
}
@Override
public void stop() {
System.out.println("------打印机停止工作了--------");
}
}
7.4 关键字:工厂方法的设计模式
interface IWorkFactory{
Work getWork();
}
class TeacherWorkFactory implements IWorkFactory{
@Override
public Work getWork() {
return new TeacherWork();
}
}
class StudentWorkFactory implements IWorkFactory{
@Override
public Work getWork() {
return new StudentWork();
}
}
interface Work{
void doWork();
}
class StudentWork implements Work{
@Override
public void doWork() {
System.out.println("学生在学习");
}
}
class TeacherWork implements Work{
@Override
public void doWork() {
System.out.println("苍老师在讲课");
}
}
7.5 关键字:内部类
一 定义 :
在一个类A中再声明一个类B,那么类A叫做外部类,类B叫做内部类。
二 内部类的分类
成员内部类 vs 局部内部类
成员内部类(静态内部类 vs 非静态内部类)
三 说明
- 内部类作为一个成员来讲:
-
1.可以使用四种权限修饰符
-
2.可以使用static关键字修饰
-
3.可以调用外部类的成员
- 内部类作为一个类来讲:
-
1.可以被继承
-
2.可以定义属性,方法,构造器。
四 如何创建内部类对象
静态内部类 : new 外部类名.内部类名()
非静态内部类 : new 外部类名().new 内部类名()
五 如何调用外部类的成员
静态内部类 :外部类名.属性名
非静态内部类 : 外部类名.this.属性名
六 局部内部类
/*
* 思考?如何在方法外获取方法内的局部内部类的对象
*
* 1.定义一个方法可以认识的接口
* 2.局部内部类实现该接口
* 3.该方法的返回值为接口的类型
* 4.返回该局部内部类的对象
*/
public P show(){
//局部内部类
class Student implements P{
public void say(){
System.out.println("aaa");
}
}
return new Student();
}
interface P{
void say();
}
7.6 关键字:枚举
一 定义
一个类的对象是可数多个的,这样的类我们称为枚举类
二 如何自定义枚举类
/*
* 自定义枚举类
*/
class Season{
//1.私有化构造器
private Season(String seasonName,String seasonDes){
this.seasonDes = seasonDes;
this.seasonName = seasonName;
}
private String seasonName;
private String seasonDes;
/*
* 快捷键 ctrl + shift + x 全部变为大写
* 快捷键 ctrl + shift + y 全部变为小写
*/
//2.创建四个对象
public static final Season SPRING = new Season("春天","春眠不觉晓");
public static final Season SUMMER = new Season("夏天","夏天蚊子咬");
public static final Season AUTUMN = new Season("秋天","秋天落叶多");
public static final Season WINTER = new Season("冬天","冬天不洗澡");
public String getSeasonName() {
return seasonName;
}
public String getSeasonDes() {
return seasonDes;
}
}
三 如何使用enum关键字定义枚举类
enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
四 常用API
五 枚举的对象实现接口中的方法
enum Season2 implements Show {
SPRING("春天"){
@Override
public void info() {
System.out.println("春天");
}
},
SUMMER("夏天"){
@Override
public void info() {
System.out.println("夏天");
}
},
AUTUMN("秋天"){
@Override
public void info() {
System.out.println("秋天");
}
},
WINTER("冬天"){
@Override
public void info() {
System.out.println("冬天");
}
};
/*
* 构造器只能为private
*/
private Season2(String name){
}
}
7.7 关键字:注解
一 定义:
可以用来对程序的一些结构(类,属性,包名,构造器)进行补充说明,而不改变原来的结构。
二 JDK内置的三个注解
- @Override :限定重写父类方法, 该注解只能用于方法
-
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
-
@SuppressWarnings : 抑制警告
三 自定义注解
格式 :
权限修饰符 @interface 注解名{
}
代码:
@interface MyAnn2{
int age() default 20; //默认值为20
String name();
}
第8章 Java常用类
8.1包装类
一 八个包装类
二 基本数据类型,包装类,String三者之间的转换
8.1String类
-
1.String类是final修饰的不能被继承
-
2.String实现了Serializable接口,可以被序列化。
-
3.String实现了Comparable接口,用来比较大小的
-
4.String实现了CharSequence接口,可以获取字符串的长度,可以获取字符串上的某一个字符
-
5.String的底层是一个字符数组,并且该数组是final修饰的。
-
6.字符串是不可变的字符序列
8.2String常用API
8.3 StringBuffer和StringBuilder
8.3.1 String,StringBuffer,StringBuilder三者之间的区别?
String : 底层是用一个char[]存储数据的而且该数组被final修饰,是一个不可变的字符序列
StringBuffer:底层是一个char[],可变的字符序列,线程安全的,效率低
StrinbBuilder:底层是一个char[],可变的字符序列,线程不安全的,效率高
8.3.2 StringBuffer构造器说明
* new StringBuffer() : 创建一个长度为16的数组
* new StringBuffer(20) : 创建一个长度为20的数组
* new StringBuffer("aaaaa") :创建一个字符串长度 + 16的数组
- 说明:
-
当我们向一个长度为16的数组中添加第17个元素时。StringBuffer底层会进行扩容。
-
扩容为原来长度的2倍+2
- 构造器使用说明:
-
1.如果知道添加元素的长度建议使用new StringBuffer(20)
-
2.如果不知道添加元素的数量 建议使用 new StringBuffer()
-
3.知道目前先添加元素的内容可能后边还有追加用 new StringBuffer("aaaaa")
- 注意 : 构造器中不能写null 否则会报空指针异常
-
8.3.3常用方法
8.4 时间日期API
8.4.1 Date
- java.util.Date
*-
两个构造器
-
new Date() : 获取当前时间
-
new Date(long time) : 获取毫秒数对应的时间
-
两个方法
-
toString() : 显示时间
-
getTime() : 获取时间对应的毫秒数
- java.sql.Date
-
一个构造器
-
new Date(long time) :获取对应毫秒数的日期
-
两个方法
-
toString() : 显示日期
-
getTime() : 获取日期对应的毫秒数
-
8.4.2 java.text.SimpleDateFormat:格式化时间
- java.text包下都是关于 国际化的类。
- 两个方法
-
- format(Date date) : 将时间转成字符串
-
- parse(String str) : 将字符串转成时间
注意 : 不同格式之间不能相同进行转换(字符串转成时间)
代码:
- parse(String str) : 将字符串转成时间
SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
//2018-07-13T11:53:02.387+0800
String time3 = sdf3.format(date);
System.out.println(time3);
System.out.println("--------------------------");
SimpleDateFormat sdf4 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//2018-07-13 14:19:44
String time4 = sdf4.format(date);
System.out.println(time4);
//将字符串转成时间
Date date2 = sdf4.parse("2018-07-13 14:19:44");
System.out.println(date2.toString());
8.4.3日历类(了解
Calendar instance = Calendar.getInstance();
// System.out.println(instance.getClass().getName());
//在今天的天数上 + 1 (正数是添加,负数是减少)
instance.add(Calendar.DAY_OF_MONTH, 1);
//获取今天是当月的第几天
int day = instance.get(Calendar.DAY_OF_MONTH);
System.out.println(day);
//将当天是当月的第几天修改成第1天
instance.set(Calendar.DAY_OF_MONTH, 1);
day = instance.get(Calendar.DAY_OF_MONTH);
System.out.println(day);
//获取日历对应的时间
Date date = instance.getTime();
System.out.println(date.toString());
8.5 其它类
8.5.1 Math:用于数学计算
8.5.2 BigInteger
说明 :Integer类作为int的包装类,能存储的最大整型值为,BigInteger类的数值范围较Integer类、Double类的数值范围要大得多,可以支持任意精度的整数。
8.5.3 BigDecimal
说明:一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。BigDecimal类支持任何精度的定点数。
第9章 异常处理
9.1 异常体系结构
* |----- Throwable
*
* |----- Error :没有针对性的代码可以处理
*
* |----- Exception :有针对性的代码进行处理
* (RuntimeException下的所有异常都是运行时异常)
* |----- 运行时异常 : 在代码运行时发生的异常,可以不用处理
* 算术运算异常 - ArithmeticException
* 空指针异常 - nullPointerException
* 下角标异常 - ArrayIndexOutofBoundsException
* |------ 编译时异常 :在代码编译时发生的异常,必须进行处理,否则不能运行
* 解析异常 : ParseException
图片:
9.2异常的处理
9.2.1 try-catch-finally
*1.trt中就写可能发生的异常的代码
*2.当执行try中的代码时,一旦发生异常。会创建异常对应的异常类的对象并抛出。会根据catch后面的异常类型依次进行匹配.一旦匹配成功,则执行异常处理的相应的代码。同时跳出try-catch结构,代码继续向下执行。
-
如果匹配不成功,则抛出异常给方法的调用者同时终止程序的运行。
*3.catch可以有多个。如果多个异常类是子父类关系,那么子类在上父类在上。如果不是子父类关系谁上谁下都可以
*4.try中的代码一旦出现异常,try中出现异常代码下面的代码将不再执行
*5.try - catch结构外不能调用try中声明的变量。因为作用域的问题
*6.finally是可选的。finally中的代码一定会执行
*7.常见异常信息 e.getMessage() e.printStackTrace()
代码:
try {
System.out.println(1 / 0);
} catch (ArithmeticException e) {
//打印异常信息
String string = e.getMessage();
System.out.println("message= " + string);
e.printStackTrace();
} finally{
System.out.println("我必须执行");
}
System.out.println("程序执行完毕");
9.2.2 throws
格式 :方法名()throws 异常类型1,异常类型2…{}
throws :
抓到异常后自己不处理,异常向上抛,让方法的调用者进行处理
throws和try-catch-fianlly的区别?
1.throws并没有真正的解决异常,最后还是需要try-catch-finally去处理异常。
2.try-catch-finally是真正的将异常进行处理。
throws和try-catch-finally的使用场景?
1.当我们真正的要求解决异常的时候使用try-catch-finally
2.当我们需要调用多个方法时,并且可能会出现异常。那么多方法建议将异常向上抛,抛给方法调用者。
9.2.3 手动抛出异常类对象 – throw
抛 :
当java程序运行时,一旦出现异常。Jvm会创建对应的异常类的对象。并抛出。抛出给方法调用者。同时终止程序的运行。
①系统向往抛异常 ②手动向往抛异常 throw
手动抛出异常throw
格式 : throw new 异常类型();
9.2.4自定义异常类
1.继承Exception那么就是编译时异常。继承RuntimeException那就是运行异常
2.写两个构造器。一个空参的。一个有参的参数为String
3.需要显示声明一个序列版本号 ,可以不声明系统会自动补充一个。
private static final long serialVersionUID = 5162710183389028792L;
代码:
public class MyException extends RuntimeException{
private static final long serialVersionUID = 51629028792L;
public MyException(){
}
public MyException(String s){
super(s);
}
}
第10章 集合
10.1 数组和集合
一 内存中对数据进行存储和管理的“容器”:数组,集合
二 数组存储的特点和缺点
1.数组的特点
①数组的长度不可变
②声明数组时的类型决定了数组元素存储的类型
2.数组的弊短
①数组的长度不可变,不能很好的进行扩容
②数组中的属性和API比较少。比如缺少 ,增,删,改,查等方法
③数组只可以用来存放有序的且可重复的数据。对无序的且不可重复的要求无能为力。
三 集合存储的优点:
1.集合的长度是可变的。
2.集合用于处理的API非常丰富,比如,增,删,改,查
3.集合底层的数据结构非常多。比如,数组,链表,红黑树
10.2 Collection接口
一 单列集合框架结构:Collection
二 Collection接口常用方法:
三 Collection集合与数组间的转换
数组—>集合:Arrays.asList(T…t)
集合—>数组:toArray()
四 使用Collection集合存储数据,要求数据所在的类满足:
需要重写equals()
10.3 Iterator接口
10.3.1遍历Collection的两种方式:
①增强for循环
②Iterator迭代器
10.3.2 java.utils包下定义的迭代器接口:Iterator
1.作用:用来遍历集合中的元素
2.如何获取实例:集合的对象名.iterator()
3.常用方法:
hasNext():是否还有下一个元素
next():①指针下移 ②获取指针指向的元素
4.举例:
Collection c = new ArrayList();
c.add(123);
c.add("aaa");
c.add("ccc");
c.add(789);
// 获取到Iterator实现类的对象
Iterator iterator = c.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
5.图示说明:
10.3.3增强for循环:(foreach循环)
格式:
for(元素的类型 临时变量 : 数组、集合的对象名){
}
遍历集合:
Collection c = new ArrayList();
c.add(123);
c.add("ccc");
c.add(789);
for(Object obj : c){
System.out.println(obj);
}
遍历数组:
String[] str = {"aa","bb","cc"};
for (String string : str) {
System.out.println(string);
}
10.4 Collection子接口-List接口
一 存储的数据特点:有序且可重复
二 常用方法:(记住)
三 常用实现类:
ArrayList : List的主要实现类。底层是使用数组实现的。线程是不安全的效率高。查找快,增删慢。
Vector : Vector是古老的实现类,底层也是使用数组实现的,线程安全效率低。查找快,增删慢。
LinkedList : 底层是使用双向链表实现的。查找慢,增删快。
四 存储的元素的要求:
自定义类需要重写equals方法
五 ArrayList
①ArrayList的说明:
new ArrayList() :底层创建一个长度为10的数组
② 如何向ArrayList中添加数据?
当我们通过一个空参的构造器创建一个对象时,底层会为我们创建一个长度为10的数组。当我们向数组添加第11个元素时,底层会进行扩容。扩容为原来数组长度的1.5倍。再将原来数组的数据放到
新的数组中。
10.4 Collection子接口-Set接口
10.4.1存储的数据特点:无序的,不可重复的
- 无序性 :指的不是随机性。根据hashCode()方法算出来的哈希值,来决定元素存储的位置。
- 不可重复性 :指是调用equals()方法后,如果返回的是true则认为两个值是相同的。如果返回的结果是false那么认为两个值不相同。
10.4.2 HashSet元素添加方式:
当我们向HashSet中添加元素a时。先根据a所在类的hashCode方法算出一个哈希值,来决定该数据在数组中存放的位置。如果该位置上没有其它元素则直接放入。如果该位置上有其它元素b。那么将会调用a所在类的equals方法
和b进行比较。如果返回值为true则认为两个数据相同则不再放入。如果返回false则以链表的形式将数据a放入该位置。b —> a
补充:当链表的数据超过8时将用红黑树的结构替换链表。
10.4.3常用方法: (没有额外的再增加其它的方法)
10.4.4常用实现类:
HashSet,LinkedHashSet,TreeSet
10.4.5存储元素所在类的要求:
HashSet , LinkedHashSet : 重写equals和hashCode方法
TreeSet : 自定义类实现Comparable或者创建Comparator实现类的对象。
10.4.6TreeSet的使用:
说明:
1.TreeSet的底层的数据结构是红黑树
2.TreeSet可以对元素安某个属性进行排序
3.TreeSet中添加的元素必须是相同的类型
思考?
1.如果即有自然排序又有定制排序谁起作用? 定制排序
2.自然排序和定制排序哪个更好? 定制排序,因为定制排序使用起来更灵活
10.4.7 LinkedHashSet的使用说明:
继承了HashSet,同样也是无序的且不可重复的。
但是可以安照添加元素的顺序进行遍历。因为底层维护了一张链表用来记录元素添加的顺序。
10.4.8HashSet的实现原理?
当我们向HashSet中添加元素a时。先根据a所在类的hashCode方法算出一个哈希值,来决定该数据在数组中存放的位置。如果该位置上没有其它元素则直接放入。如果该位置上有其它元素b。那么将会调用a所在类的equals方法
和b进行比较。如果返回值为true则认为两个数据相同则不再放入。如果返回false则以链表的形式将数据a放入该位置。b —> a
补充:当链表的数据超过8时将用红黑树的结构替换链表。
10.5 Map接口(双列集合框架)
10.5.1 存储数据特点:存取的是键值对
图示:
10.5.2 常用实现类:
10.5.3 常用方法
说明 :
1.可以把Map中所有的key看成是Set的集合,无序且不可重复。
如果key中的元素是自定义类需要-重写hashCode和equals方法
2.可以把Map中所有的value看成是Collection的集合,无序且可重复。
如果value中的元素是自定义类那么需要重写equals方法
3.可以把Map中键值对(Entry)看成是一个Set集合,无序且不可重复。entry的位置是根据key的位置来决定的。
10.5.4 面试题
①[面试题1]:HashMap和Hashtable对比?
HashMap: Map的主要实现类,线程不安全的,效率高。
可以存储null的key和value
Hashtable: 线程安全的,效率低。不可以存储null的key和value
②[面试题2]:HashMap的底层实现原理?
当我们向HashMap中添加元素(K1,v1)时,先根据k1的hashCode方法算出的哈希值,来决定存储的位置。如果该位置上没有其它元素则直接存入。如果该位置上已经有其它元素(k2,v2)。那么将会调用k1的equals方法
和k2进行比较。如果返回值是true,说明key值相同那么将v1替换v2。如果返回值是false说明key不相同。那么将以链表的形式将(k1,v1)和(k2,v2)链接到一起。当链表的数据达到8时将链表替换成红黑树。
10.5.5 结构说明
new HashMap() :
new HashMap(); 底层创建了一个Node类型的数组,长度为16,加载因为子为0.75。当集合中的元素超过12时便会扩容,扩容为原来的2倍
说明:
1.HashSet的底层就是一个HashMap.向HashSet中添加的元素,实际上是添加到HashMap中的key里HashSet的特性就是HashMap中key的特性。
2.LinkedHashSet 底层就是LinkedHashMap
3.TreeSet的底层其实就是TreeMap
10.5.6 读取配置文件的操作实现
//第一步创建对象
Properties properties = new Properties();
//第二步创建输入流的对象
FileInputStream fis = new FileInputStream("person.properties");
//第三步 加载流
properties.load(fis);
//第四步 读取配置 文件
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username + " " + password);
//第五步 关流
fis.close();
10.5.7 Collections工具类的使用
第11章 泛型
11.1泛型在集合中的使用
11.1.1在集合中使用泛型之前的例子
List list = new ArrayList();
// list.add(123);
// list.add("aaa");
// list.add(new Person());
11.1.2在集合中使用泛型例子1
List<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
11.1.3在集合中使用泛型例子2
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("aa", 111);
Set<Entry<String,Integer>> entrySet = map.entrySet();
for (Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
11.2自定义泛型类,泛型接口,泛型方法
11.2.1自定义泛型类
public class Person<T> {
T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
思考?如何让子类指明父类的泛型类型
1.子类继承父类后,可以直接指明父类的泛型类型。
例 : public class Women extends Person
2.子类继承父类后,不指明父类的泛型类型,而是在创建子类对象时指明父类的泛型类型
例 :public class Women extends Person
Women w = new Women<>();
11.2.2自定义泛型接口
public interface English<T> {
void setT(T t);
}
11.2.3自定义泛型方法
格式 : 权限修饰符 E 方法名(E e){
}
<E> : 告诉编译器参数中的E是一个泛型
E : 泛型的类型
当我们向方法中传入数据,数据的类型是什么,那么泛型的类型就是什么。
说明:
1.在try-catch结构中 : 捕获的异常类型不能使用泛型
2.不能将泛型类指明的泛型 ,用static修饰。不能在静态方法中使用。
3.泛型方法可以是静态方法
public <E> E setE(E e){
return e;
}
11.3泛型与继承的关系
1.如果 A 类 是B 类的父类 那么 A
2.如果 A 类 是B 类的父类那么G
和 G不存在子父类关系
11.4通配符 - ?
11.4.1通配符的使用说明:
说明:
通配符: ? ?是所有泛型的父类。
1.使用通配符<?>的集合只能存储null 2.使用通配符<?>的集合可以遍历所有的数据,类型都是Object
11.4.2通配符的限制说明:
? extends Number : 只能接收Number及Number子类的泛型类型
? super Number : 只能接收Number及Number父类的泛型类型
? extends Comparable : 只允许泛型为实现Comparable接口的实现类的类型
第12章 IO流
12.1File类
12.1.1.File类的理解
1.File用来表示一个文件(.txt .mp3 .mp4 .avi)还可以代表一个目录
2.File只可以用来创建目录文件等操作。没有向文件写内容的功能。
3.File类是在java.io包下的
4.往往我们把File看成是流的端点
5.往往我们将File的对象作为参传传递给流的对象的构造器中。
12.1.2.如何实例化
//表示的就是aaa.txt这个文件
File file = new File("E:\\io\\aaa.txt");
//相当于是 E:\\io\\aaa.txt
new File("E:\\io","aaa.txt");
绝对路径:包含盘符在内的完整路径
相对路径:相对于当前项目的路径
12.1.3.常用方法
注意:蓝色字体的API要掌握
12.2 IO流概述
12.2.1.流的分类
1)流的流向:输入流,输出流
2)流中数据单位:字节流,字符流
3).流的角色不同:节点流,处理流
图示:
12.2.2.流的体系结构
12.3节点流(文件流
//第一步 创建File对象
File reader = new File("456.txt"); //读取的内容
File writer = new File("789.txt"); //接收内容的文件
//第二步创建流
FileInputStream fis = new FileInputStream(reader);
FileOutputStream fos = new FileOutputStream(writer);
//第三步 一边读一边写
byte[] b = new byte[500];
int len = 0;
while((len = fis.read(b)) != -1){
//写数据
fos.write(b, 0, len);
}
//关流
fis.close();
fos.close();
字符流:
File file = new File("bbb.txt");
File file2 = new File("ccc.txt");
FileReader reader = new FileReader(file);
FileWriter writer = new FileWriter(file2);
char[] c = new char[100];
int len = 0;
while((len = reader.read(c)) != -1){
writer.write(c, 0, len);
}
writer.close();
reader.close();
12.4缓冲流的使用
public void copy(String src,String desc){
//第一步创建File对像
File reader = new File(src); //读的内容
File writer = new File(desc); //写的内容
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//第二步创建流
fis = new FileInputStream(reader);
bis = new BufferedInputStream(fis);
fos = new FileOutputStream(writer);
bos = new BufferedOutputStream(fos);
//第三步 一边读一边写
byte[] b = new byte[1024]; //用来接收数据使用的
int len = 0; //用来表示文件是否读取完毕
while ((len = bis.read(b)) != -1) {
//写数据
bos.write(b, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//第四步 关流
try {
if(bos != null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bis != null){
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis != null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
File file = new File("aaa.txt");
File file2 = new File("fff.txt");
FileReader reader = new FileReader(file);
FileWriter writer = new FileWriter(file2);
BufferedReader br = new BufferedReader(reader);
BufferedWriter bw = new BufferedWriter(writer);
/*
char[] c = new char[100];
int len = 0;
while((len = br.read(c)) != -1){
bw.write(c, 0, len);
}
*/
String str = null;
while((str = br.readLine()) != null){
bw.write(str);
}
bw.close();
br.close();
writer.close();
reader.close();
12.5转换流的使用
作用:
- 读取时,将字节流转成字符流,写入时,将字符流转成字节流
2.将读取的文件内容的编码集(gbk),在写入到另一个文件时将编码集进行改变(utf-8)。
注意:文件内容的编码集格式必须和InputStreamReader设置的编码集格式一样。
代码:
//创建字节流
FileInputStream fis = new FileInputStream("char8.txt");
//读取内容时 - 将字节流转成字符流
InputStreamReader isr = new InputStreamReader(fis,"gbk");
//创建字符流
FileOutputStream fos = new FileOutputStream("999.txt");
//写入内容时 - 将字符流转成字节流
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
char[] c = new char[100];
int len = 0;
while((len = isr.read(c))!=-1){
osw.write(c, 0, len);
}
osw.close();
isr.close();
fos.close();
fis.close();
图示:
12.6编码集
12.7其它流的使用(了解)
12.7.1 标准输入输出流
//将字节流转成字符流
InputStreamReader isr = new InputStreamReader(System.in);
//创建BufferedReader
BufferedReader br = new BufferedReader(isr);
while(true){
//一行一行的读取内容
String str = br.readLine();
if("e".equals(str) || "exit".equals(str)){
break;
}
//转成大写进行输出
System.out.println(str.toUpperCase());
}
//关流
br.close();
isr.close();
12.7.2 打印流
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("text.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
PrintStream ps = new PrintStream(fos, true);
if (ps != null) { // 把标准输出流(控制台输出)改成文件
//将输入到控制台的那个对象(那根管道) 换成指向text.txt文件的那根管道了
System.setOut(ps); //只能赋值一次
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print('a');
}
ps.close();
}
12.7.3 数据流
注意:
1.读取文件时,文件中应该先写入内容
2.写入文件内容的类型要和读取文件内容的类型保持一致 (否则会出现乱码 或者 异常)
@Test
public void test() throws Exception{
FileInputStream fis = new FileInputStream("data.txt");
DataInputStream dis = new DataInputStream(fis);
//读取内容
String name = dis.readUTF();
int age = dis.readInt();
char sex = dis.readChar();
System.out.println(name + " " + age + " " + sex);
//关流
dis.close();
fis.close();
}
@Test
public void test2() throws Exception{
FileOutputStream fos = new FileOutputStream("data.txt");
DataOutputStream dos = new DataOutputStream(fos);
//写内容
dos.writeUTF("aaa");
dos.writeInt(123);
dos.writeChar('?');
//关流
dos.close();
fos.close();
}
12.8 对象流的使用
说明:
1.需要被序列化的对象,必须实现Serializable接口
2.类中的属性,除了基本数据类型外。引用数据类型都必须也实现Serializable
3.显示的声明一个serialVersionUID
注意:static 修饰的 和 transient 修饰的属性不能序列化
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
代码:
/*
* 序列化
*/
@Test
public void test() throws Exception{
FileOutputStream fos = new FileOutputStream("123.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person person = new Person("cc",16,new Address("国王街",38));
oos.writeObject(person);
oos.close();
fos.close();
}
/*
* 反序列化
*/
@Test
public void test2() throws Exception{
FileInputStream fis = new FileInputStream("123.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
Person person = (Person) obj;
System.out.println(person.toString());
System.out.println(person.address.doorID);
ois.close();
fis.close();
}
12.9 RandomAccessFile类的使用
需求:在 abcdefg中 c的后面插入DDD结果为abcDDDdefg
* 1.将指针移动到c的后面
* 2.读取c后面的所有数据,并用临时变量进行存储
* 3.指针回移
* 4.写入要插入的内容
* 5.写入临时保存的所有数据
代码:
RandomAccessFile raf = new RandomAccessFile("111.txt", "rw");
// * 1.将指针移动到c的后面
raf.seek(3);
// * 2.读取c后面的所有数据,并用临时变量进行存储
String str = raf.readLine();
// * 3.指针回移
raf.seek(3);
// * 4.写入要插入的内容
raf.write("DDD".getBytes());
// * 5.写入临时保存的所有数据
raf.write(str.getBytes());
raf.close();
第13章 多线程
13.1 基本概念
13.1.1 程序:为了完成某项特定任务,使用某种语言,编写一组指令的集合。
13.1.2 进程:一个正在进行中的程序
13.1.3 线程:在一个进程中,执行的一套功能流程,称为线程。在一个进程中,执行的多套功能流程,称为多线程。
13.2 为什么使用多线程
13.2.1 抢占式策略系统
系统会为每个执行任务的线程,分配一个很小的时间段,当该时间段用完后,系统会强制剥夺其 cpu 的使用权,交给其他的线程执行任务。
1.提高程序效率,提高效率的方式是尽可能的利用 cpu 的资源
2.提高用户体验
13.3 创建执行线程的方式(四种)
13.3.1 创建执行线程的方式一
- 声明一个类继承 Thread 类
- 重写 Thread 的run() 方法,同时编写线程执行体
- 创建该子类的实例
- 调用 start() 方法启动线程,默认调用 run()
13.3.2 创建执行线程的方式二
- 声明一个类实现 Runnable 接口
- 实现接口中的 run() 方法,同时编写线程执行体
- 创建该实现类的实例
- 将该实例作为参数,传递给 Thread 的构造器
- 调用 Thread 类的 start() 方法启动线程,默认执行 run() 方法
13.3.3 继承 Thread 类与实现 Runnable 接口的区别
- 当多个线程需要访问共享数据时,首选使用实现 Runnable 接口方式
- 实现接口方式解决了 Java 中单继承的局限性
13. 4 多线程的常用方法
currentThread() : 获取当前线程
getName() : 获取线程名称
setName() : 设置线程名称
start() : 启动线程
sleep(long millis) : 是一个静态方法,使当前线程进入睡眠状态
join() / join(long millis) : 是一个实例方法,使当前线程进入阻塞状态
interrupt() : 中断阻塞状态的线程,使阻塞状态产生一个 InterruptedException
isAlive() : 判断线程是否处于存活状态
yield() : 线程让步,主动放弃 cpu 的资源
13.5 线程的优先级
优先级共 1-10 个级别,默认优先级为 5。优先级高并不意味着线程一定会有限执行,只不过更高概率的获取 cpu 的资源。
getPriority() : 获取线程优先级
setPriority() : 设置线程优先级
MAX_PRIORITY : 10
NORM_PRIORITY : 5
MIN_PRIORITY : 1
13.6 线程的生命周期
13.7 线程同步
案例:模拟售票程序,实现三个窗口同时售票 100张
遇到问题:当多个窗口同时访问“共享数据”时,产生了无序、重复、负数等多线程安全问题
解决办法:将多个线程同时放的“共享数据”,包装起来视为一个整体,确保一次只有一个线程执行流访问共享数据,加锁。
Java 针对上述问题提供了相应的解决办法:
1.同步代码块
synchronized(同步监视器){
//需要访问共享数据
}
同步监视器:俗称“锁”,可以使用任意对象充当,但是必须保证多个线程持有同一把锁(同一个对象)
package com.atguigu.java.synchronized1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable{
int tick = 100;
Object obj = new Object();
Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
synchronized(obj){
if(tick > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
}
}
}
}
}
2.同步方法:只需在方法的声明处,加 synchronized 关键字
package com.atguigu.java.synchronized1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable{
int tick = 100;
Object obj = new Object();
Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
show();
}
}
public synchronized void show(){
if(tick > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
}
}
}
非静态同步方法的隐式锁为:this
静态同步方法的隐式锁为:Class 实例
3.同步锁 Lock
a)lock() 上锁
b)unlock() 释放锁,必须保证手动的释放锁,因此 unlock() 需要使用在 finally 中
package com.atguigu.java.synchronized1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable{
int tick = 100;
Object obj = new Object();
Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
l.lock();
try{
if(tick > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
}
}finally{
l.unlock(); //释放锁
}
}
}
}
13.8 线程通信(线程交互)
当多个线程完成某些任务时,有时多个线程之间也需要一定的通信,即线程通信
线程通信的核心是 Object 类中提供的三个方法:
wait() : 使当前同步监视器上的线程进入等待状态,等待的同时释放锁
notify()/notifyAll() : 唤醒当前同步监视器上一个/所有等待状态的线程
注意:上述方法必须使用在 synchronized 中(Lock 同步锁的线程通信方式:Condition)
package com.atguigu.java.waitandnotify;
public class HelloThread implements Runnable{
int i = 0;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj) {
obj.notify();
if(i <= 100){
System.out.println(Thread.currentThread().getName() + ":" + i++);
}
try {
obj.wait();
} catch (InterruptedException e) {
}
}
}
}
}
【线程通信的经典案例】生产者消费者案例(必须会)
第15章 网络编程
15.1 InetAddress类的使用
代码:
/*
* 记住:开发的时候全部抓异常
*/
@Test
public void test() throws Exception{
//获取本地的地址
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
//获取域名
System.out.println(localHost.getHostName());
//获取IP地址
System.out.println(localHost.getHostAddress());
}
/*
* 获取网络地址
*/
@Test
public void test2() throws Exception{
InetAddress byName = InetAddress.getByName("www.baidu.com");
System.out.println(byName);
//获取域名
System.out.println(byName.getHostName());
//获取IP地址
System.out.println(byName.getHostAddress());
}
15.2 UDP
代码:
@Test
public void client() throws Exception {
// 创建DatagramSocket
DatagramSocket socket = new DatagramSocket();
byte[] bytes = "hello how are you".getBytes();
/*
* bytes : 要发送的数据
* bytes.length : 发送数据的长度
* InetAddress.getLocalHost() : 服务器的地址
* 3399 : 服务器的端口号
*/
// 创建数据包、数据报
DatagramPacket p = new DatagramPacket(bytes, bytes.length,
InetAddress.getLocalHost(), 3399);
// 发送数据
socket.send(p);
// 关闭
socket.close();
}
@Test
public void server() throws Exception {
// 创建DatagramSocket
DatagramSocket socket = new DatagramSocket(3399);
//将接收到的数据保存到数组中
byte[] b = new byte[1024];
DatagramPacket p = new DatagramPacket(b, b.length);
//接收数据
socket.receive(p ); //将接收到的数据都放入到数据包中
/*
* p.getLength() : 接收到的数据的长度
*/
System.out.println(new String(b,0,p.getLength()));
//关闭
socket.close();
}
15.3 TCP
代码:
/*
* Client
*/
@Test
public void client() throws Exception, Exception{
//创建一个Socket - 端口号必须和服务器的端口号一致
Socket socket = new Socket(InetAddress.getLocalHost(), 4567);
//发送数据
OutputStream os = socket.getOutputStream();
os.write("您好服务器我是客户端".getBytes());
//通知服务器数据发送完毕
socket.shutdownOutput();
//接收服务器端的数据
InputStream is = socket.getInputStream();
byte[] b = new byte[100];
int len = 0;
while((len = is.read(b)) != -1){
System.out.println("客户端 :" + new String(b,0,len));
}
//通知服务器数据接收完毕
socket.shutdownInput();
//关流
os.close();
is.close();
socket.close();
}
/*
* Server
* 注意 : 服务器端只能开启一次,否则会报异常
*/
@Test
public void Server() throws Exception{
//创建一个ServerSocket
ServerSocket serverSocket = new ServerSocket(4567);
//接收客户端的请求
Socket socket = serverSocket.accept();
//接收客户端的数据
InputStream is = socket.getInputStream();
//读取数据
byte[] b = new byte[100];
int len = 0;
while((len = is.read(b)) != -1){
System.out.println("服务器:" + new String(b,0,len));
}
socket.shutdownInput();
//给客户端发送数据
OutputStream os = socket.getOutputStream();
os.write("小样是你啊".getBytes());
socket.shutdownOutput();
//关流
socket.close();
os.close();
serverSocket.close();
}
15.4 URL
一 URL构成
二 构造器:
三 API
四 针对HTTP协议的URLConnection类
URL url = new URL("http://192.168.10.150:8080/txt/123.txt");
//获取一个连接
URLConnection urlConnection = url.openConnection();
HttpURLConnection conn = (HttpURLConnection) urlConnection;
conn.connect(); //连接网络
//获取响应码 - 如果成功返回200
int responseCode = conn.getResponseCode();