JAVA概述
1. JAVA语言执行机制
先编译,后执行,将Java文件编译为平台中立的class(字节码文件、二进制文件)文件。
Write Once Run Anywhere 一次编译 运行在任何地方
2. 名词解释
JVM(Java Virtual Machine)虚拟机:使用软件在不同操作系统中,模拟相同的环境。
JRE(Java Runtime Environment)运行环境:包含JVM和解释器,完整的Java运行环境。
JDK(Java Development Kit)开发环境:包含JRE + 类库 + 开发工具包(编译器+调试工具)。
3. DOS命令
更换盘符: d:
查看当前目录下的文件及文件夹:dir
进入文件夹: cd 文件夹的名字
返回上一级目录:cd ..
清空屏幕:cls
删除文件:del 文件名
删除文件夹:rd 文件夹名称
退出: exit
查看本机ip地址:ipconfig
4. 类的阐述
同一个源文件中可以定义多个类。
编译后,每个类都会生成独立的 .class文件。
一个类中,只能有一个main方法,每个类都可以有自己的main方法。
public修饰的类称为公开类,要求类名必须与文件名称完全相同,包括大小写。
一个源文件中,只能有一个公开类。
5. 注释
单行注释 //
多行注释 / /
文档注释
/*
*/
javadoc -d . 文件名(带后缀)生成帮助文档,只需要查看index.html就可以
JAVA语言基础
1. 变量
1.1 定义方式
先声明,再赋值:【常用】数据类型 变量名;变量名 = 值;
声明并赋值:【常用】数据类型 变量名 = 值;
多个同类型变量的声明与赋值:【了解】数据类型 变量1 , 变量2 , 变量3 = 值3 , 变量4 , 变量5 = 值5;
1.2 单位换算
1TB = 1024GB
1GB = 1024MB
1MB = 1024KB
1KB = 1024byte
1byte = 8bit
bit 称之为 位 一个字节(byte)占8位 计算机只认二进制的数值 也就是说每一位只能存储一个0或者1
1.3 整型
面试题:
byte的取值范围和原理?
取值范围 -128 ~ 127
因为一个byte占8位,每一位可以存储一个0或者1,计算机以首位(最高位)为符号位,0表示正数,1表示负
数,所以byte最大的数值为0111 1111 转换为十进制为127,最小的数值为1000 0000 转换为十进制为-128
1.4 浮点类型
1.5 布尔类型
1.6 字符类型
char类型可以使用三种赋值方式
字符赋值:char c1 = ‘A’;(通过’’描述为字符赋值)
整数赋值:char c2 = 65;(通过十进制数65在字符集中对应的字符赋值)
进制赋值:char c3 = ‘\u0041’;(通过十六进制数41在字符集中所对应的字符赋值)
2. 转义字符
3. String类型
4. 类型转换
4.1 自动转换
4.2 强制转换
5. 运算符
5.1 算术运算符
++ 表示自增1
++单独作为一条语句书写 在前在后没有任何区别
不是作为一条语句单独执行 ++在前表示 先++ 再执行其他
++在后 表示先执行其他的 再++
— 表示自减1
—不是作为一条语句单独执行 —在前 表示先— 再执行其他
—在后 表示先执行其他的 再—
5.2 赋值运算符
5.3 关系运算符
5.4 逻辑运算符
&& 短路与 表示要求两个或者多个表达式同时成立 则结果为真,如果第一个表达式不成立 则后续的表达式不再执行
& 与 表示要求两个或者多个表达式同时成立 则结果为真 没有短路的效果
不管第一个表达式结果如何 都将执行完所有的表达式
|| 短路或 表示要求两个或者多个表达式有一个成立 则结果为真,如果第一个表达式成立 那么后续的不再执行
| 或 表示要求两个或者多个表达式有一个成立 则结果为真
| 没有短路的效果 不管第一个表达式结果如何 都将执行完所有的表达式
5.5 三元运算符
6. Scanner接收输入信息
程序运行中,可在控制台(终端)手动录入数据,再让程序继续运行。
使用顺序:
1.导入 java.util.Scanner。
2.声明 Scanner 类型的变量。
3.使用Scanner类中对应的方法(区分类型):
.nextInt(); //获得整数
.nextDouble(); //获得小数
.next(); //获得字符串
.next().charAt(0);//获得单个字符(了解)
注意:如果输入了不匹配的数据,则会产生java.util.InputMismatchException
分支结构
1. 包
包的概念:
包用于方便管理Java文件 以及可以存放同名的Java文件
包的规则:
域名倒置 全部小写 不能以点开头或者结尾 可以包含点
举例:com.qfedu.name com.baidu.name cn.sina.name
包的声明永远在类的第一行
导包其次
2. 变量命名
变量的命名规则:
字下美人数骆驼 有意义
可以以字母、下划线、美元符号、人民币符号开头
可以包含数字,不能数字开头
见名知意
3. if结构
if:如果
if(布尔表达式){
// 代码1
}
后续代码
如果条件成立 执行代码1 然后执行后续代码
如果条件不成立 直接执行后续代码
4. if-else结构
if:如果
if(布尔表达式){
// 代码1
}else{
// 代码2
}
后续代码
如果条件成立 执行代码1 然后执行后续代码
如果条件不成立 执行代码2 再执行后续代码
5. 多重if结构
语法:
if(布尔表达式1){
//代码块1
}else if(布尔表达式2){
//代码块2
}else if(布尔表达式3){
//代码块3
}else{
//代码块4
}
6. 嵌套if结构
if(外层表达式){
if(内层表达式){
//内层代码块1
}else{
//内层代码块2
}
}else{
//外层代码块
}
7. switch结构
switch(变量|表达式){
case 值1:
逻辑代码1;
case 值2:
逻辑代码2;
case 值n:
逻辑代码n;
default:
未满足时的逻辑代码;
}
支持的数据类型 byte short int char String(JDK7) 枚举
循环结构
1. 局部变量
局部变量 | 描述 |
---|---|
赋值 | 必须先赋值才能使用 |
定义位置 | 定义在方法体内部 |
作用范围 | 离当前变量最近的大括号以内 |
重名 | 重合的作用范围不能重名 |
生命周期 | 随着方法的入栈(压栈)而生效 随着方法的出栈(弹栈)而死亡 |
存储位置 | 基本数据类型:变量和值 都存储在栈中 ;引用数据类型:栈中存放变量名(引用) 堆中存方法值 |
2. 堆(heap)和栈(stack)
栈 先进后出 FILO first in last out
堆 先进先出 FIFO first in first out
3.循环
3.1 while循环
while:当 ……
循环必要条件:
1.计数器初始化
2.判断条件
3.循环体
4.计数器变化
while循环特点:
while循环可以处理循环次数确定的情况 ,更擅长处理循环次数不确定的情况
如果条件不成立 一次都不会执行
3.2 do-while
先执行 后判断 不管条件是否成立 至少执行一次
while和do-while的区别?
while 先判断后执行 如果条件不成立 一次都不执行
do-while 先执行 后判断 不管条件是否成立 至少执行一次
do-while更擅长处理循环次数不确定的情况
3.3 for循环
for 为了…… 做
只能用于处理循环次数确定的情况 ,书写比while或者do-while更加简洁
for循环执行顺序
第一轮:
1.计数器初始化 并且只执行一次
2.条件判断
3.循环体
4.计数器变化
第二轮:
直接从判断条件开始继续执行
4. break关键字
使用break中断循环 模拟跑步停止
break
适用场景:
switch 表示跳出switch结构
循环 表示中断当前循环 没有执行完的次数 不再执行
5. continue关键字
continue 继续
只能用于循环中 表示跳出本次循环 继续执行下一次循环
统计分数大于80的学生人数占比
break和continue区别
应用场景区别:
1.break可以用于switch或者循环 continue只能用于循环
2.break在循环中表示中断循环 没有执行完的次数不再执行 continue表示跳出本次循环 继续执行下一次
方法
1. 概念
2. 形参和实参
形参:方法定义的时候书写的参数,规定了参数的个数、类型、顺序
实参:方法调用的时候传入的参数,遵守形参的规定
3. 返回值和返回值类型
void表示方法没有任何返回值,如果需要方法执行完毕以后有返回值给调用者,必须使用其他的类型来代替void
可以书写的类型:八种基本数据类型,引用数据类型
return表示中断方法并且返回内容
return关键字用法1:如果返回值类型不是void,必须使用return关键字返回对应类型的数据
return关键字用法2:如果需要在分支结构中return值,必须保证每一种都有返回值
return关键字用法3:在返回值类型为void的方法中return,此时return只表示中断方法,并且return关键字之后不能有任何内容
数组
1. 数组的概念
在内存中一块连续的空间,存储数据类型相同的内容,长度是固定的。
2. 数组的定义
1.先声明、再分配空间:
数据类型[] 数组名;
数组名 = new 数据类型[长度];
2.声明并分配空间:
数据类型[] 数组名 = new 数据类型[长度];
3.声明并赋值(繁):
数据类型[] 数组名 = new 数据类型[]{value1,value2,value3,…};
4.声明并赋值(简):
数据类型[] 数组名 = {value1,value2,value3,…}; //显示初始化,注意:不可换行
3. 数组的访问
数组的访问:赋值和取值
通过下标访问数组的元素 下标从0开始 依次加1
4. 数组的遍历
数组的遍历:逐一将数组中的元素进行访问
数组的属性:length 表示数组的长度 是一个int类型的数值
使用方式:数组名.length
在遍历数组的使用 推荐使用length作为判断条件 更准确 不会出错
5. 数组的默认值
数组的默认值
整数:0
小数:0.0
字符:\u0000
布尔:false
其他:null
6. 数组的复制
- 循环实现
2.System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度)
3.Arrays.copyOf(原数组,新长度)7. 值传递和引用传递
值传递和引用传递的区别?
基本数据类型参数传递传递的是值本身 不会影响原来的变量
引用数据类型参数传递传递的是地址 会影响原来的变量
String类型属于特殊的引用数据类型 作为参数传递原来的不会改变排序
1. 冒泡排序
```java package com.qfedu.test2;
import java.util.Arrays;
/**
- 冒泡排序
- 希尔排序 快速排序 堆排序 桶排序 选择排序 猴子排序
- 排序:将乱序的一组数列经过算法 排列为升序或者降序
- 冒泡排序:两两相邻比较 遇到条件成立(大于或者小于)就交换位置
- 经过作图分析:
- 比较的轮数是长度-1 外层循环控制比较的轮数
- 每一轮比较的次数 最多的一次是长度-1 依次递减 内层循环控制比较的次数
- 外层循环 N - 1
- 内层循环 N - 1 - i
- @author WHD
/
public class Test1 {
public static void main(String[] args) {
} } ```int a = 10;
int b = 20;
int c = a;
a = b;
b = c;
int [] nums = {20,1,55,784,10,233,65};
for(int i = 0;i < nums.length - 1;i++) {
for(int j = 0;j < nums.length - 1 - i;j++) {
if(nums[j] < nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(nums));
2. 选择排序
```java package com.qfedu.test3;
import java.util.Arrays;
/**
- 选择排序
- 冒泡的方式三个人比高低个:广坤 180 赵四 188 刘能 155
- 比较方式:使用一个元素去其他的元素进行比较 遇到需要交换位置的元素
- 先不交换 等到一轮比较完以后 交换一次位置
- 比较的次数与冒泡是一样的 但是 交换位置的次数 大大减少了
- 长度-1 的元素都将充当一遍比较的数A
- 从第二个元素开始 都将充当一遍比较的数B
- 外层循环 是比较的数A
- 内层循环 是比较的数B
@author WHD / public class Test1 { public static void main(String[] args) {
int [] nums = {20,1,55,784,10,233,65};
for(int i = 0;i < nums.length - 1;i++) {
int minIndex = i;
for(int j = i + 1;j < nums.length;j++) {
if(nums[minIndex] > nums[j]) {
minIndex = j;
}
}
if(minIndex != i) {
int temp = nums[minIndex];
nums[minIndex] = nums[i];
nums[i] = temp;
}
}
System.out.println(Arrays.toString(nums));
3. JDK自带排序
面向对象
1. 类和对象的关系
2. 属性和方法
属性:一些事物共有的特征,称之为属性
方法:一些事物共有的动作,称之为方法3. 实例变量
| 实例变量 | 描述 | | —- | —- | | 定义位置 | 类中 | | 作用范围 | 整个类中 | | 默认值 | 有默认值,与数组相同 | | 重名 | 可以与局部变量重名,局部变量优先使用,就近原则 | | 生命周期 | 随着对象的的创建而存在 随着对象被垃圾回收而死亡 GC Garbage Collection | | 存储位置 | 实例变量存储在堆中 |
4. 方法重载
1.同一个类中
2.方法名称相同
3.参数列表不同 包括参数的个数、类型、顺序 有一个不同即可称之为不同
4.跟返回值、访问权限修饰符 无关
5.构造方法
5.1 概念
构建 创造 用于创建对象的特殊方法 不能通过.调用 只能在创建的对象的时候调用
默认调用无参构造
构造方法书写格式:访问修饰符 + 类名(形参列表){}
普通方法书写格式:访问修饰符 + 返回值类型 + 方法名称(形参列表){}
5.2 无参构造
5.3 有参构造
我们在创建对象的同时,可以给属性赋值,使用有参构造
无参构造是默认存在 如果书写了有参构造 那么默认的无参将被覆盖
如果想要使用 显式的书写
5.4 构造方法重载
与普通方法重载规则类似
构造方法都是类名,没有返回值,与访问权限无关
所以只要求,参数列表不同 参数的个数 类型 顺序
6. this关键字
this关键字 表示当前对象
this关键字适用场景:
1.访问属性
2.访问方法
3.访问构造方法 this访问构造方法必须在其他构造方法中的第一句 只能使用一次
封装和继承
1. 封装
封装 :
1.将类的信息尽可能的隐藏在类的内部 不让外部直接访问
使用private修饰属性即可
被private修饰的属性表示只能在本类中访问 其他类不能访问
2.而是提供一对公开的getter和setter方法用于访问属性
2. 访问权限修饰符
2.1 类的访问权限修饰符
1.public 本项目中任何位置都可以访问
2.默认不写 本包中可以访问
2.2 类成员的访问权限修饰符
1.private 表示私有的本类中可以访问
2.默认不写的 本包中可以访问
3.protected 受保护的 本类中 本包中 子类中
4.public 本项目中任何位置都可以访问
3. static关键字
3.1 static适用场景
static可以用来修饰成员变量 静态变量,可以直接通过类名访问
成员方法 静态方法,可以直接通过类名访问
代码块 静态代码块,当Java虚拟机加载类时,就会执行该代码块
3.2 static修饰变量
被static修饰的变量在内存中只有一个拷贝,可以用于数据共享,实现多个实例之间的数据传递
类内部,可在任何方法内直接访问静态变量
其他类中,可以直接通过类名访问
3.3 static修饰方法
静态方法:可直接通过类名访问
静态方法中不能使用this和super
不能直接访问所属类的实例变量和实例方法
可直接访问类的静态变量和静态方法
实例方法:通过实例访问
可直接访问所属类的静态变量、静态方法、实例变量和实例方法
静态方法必须被实现
3.4 static修饰静态代码块
静态代码块
随着类被加载执行 多个静态代码块按照书写顺序执行 每个只执行一次 因为类只加载一次
静态代码块用于实现一些前置的操作 比如数据初始化 等
普通代码块 每new一次对象 执行一次
3.5 访问规则
1.静态与静态直接调用
2.静态访问非静态 必须先new对象
3.非静态的访问静态的 直接调用
4. 继承
继承是Java中实现代码重用的重要手段之一。Java中只支持单根继承,即一个类只能有一个直接父类.。
子类与父类是is-a的关系,子类是父类
父子类信息编写原则:
父类中编写共有的属性和行为
子类中编写独有的属性和行为
4.1 super关键字
super关键字:表示父类对象
可以访问,父类访问权限允许的
方法
属性
构造方法
super关键字:表示父类对象
构造方法
创建子类对象时,默认调用父类的无参构造方法
除非子类显式的调用父类的有参构造方法
子类必须调用父类的构造方法 无参或者有参必须调用一个
4.2 方法重写
1.父子类之间的
2.方法名称相同
3.参数列表相同
4.访问权限不能严于父类 不能窄化父类的访问权限
5.返回值相同 或者是其子类
6.父类的静态方法可以被继承 但是不能被重写 非静态方法不能重写为静态方法
7.不能抛出比父类更多的异常
@Override注解 此注解可以用于子类的方法上 表示此方法为重写父类的方法 如果没有符合以上重写的规则 那么将编译报错
4.3 object类
Object是所有的类的父类,所有的类将默认继承自此类
此类中提供了一些常用的方法,实际开发中我们经常重写这些方法
4.4 重写toString()
我们直接打印一个对象将默认调用此对象的toString方法,返回值为包名类名+@+哈希值
如果我们不想要这个效果,可以重写toString方法
public String toString() {
String str = super.toString();
return "Student[name=" + name + ",age=" + age + "]" + str;
}
4.5 重写equals()
==和equals的区别?
== 比较基本数据类型 比较的是值 比较引用数据类型比较的是内存中的地址
equals 只能比较引用数据类型 本身也使用==比较 两个对象在内存中的地址
String类对equals方法进行了重写 改为了比较内容
我们也可以对equals方法 进行重写 按照我们自己的比较规则来比较
public boolean equals(Object obj) {
// 第一步 先比较地址 如果地址相同 则直接return true 不需要继续比较 了
if(this == obj) {
return true;
}
// 代码执行到这里 表示 地址不同 那么我们应该比较内容 名字和身份证号
// 因为obj是父类对象 而父类对象不能直接获取name属性的值 所以需要强制向下转换
Person p1 = (Person) obj;
if(this.getName().equals(p1.getName()) && this.getIdCard().equals(p1.getIdCard())) {
return true;
}
// 如果代码能够执行到这里 表示 上述条件不成立
return false;
}
4.6 重写hashCode()
重写hashCode方法
hashCode是根据地址 等一些信息计算出来的一个int类型的数值
杂凑算法特点 正向是快速的 不可逆的
为什么要重写hashCode ?
因为在一些散列数据结构中,如果两个对象使用equals比较为true
那么通常hashCode也要相同
public int hashCode() {
// 权重 为31 表示计算hashCode的决定性因素
int prime = 31;
int result = 1;
result = result * prime + (this.getName() == null ? 0 : this.getName().hashCode() );
result = result * prime + (this.getIdCard() == null ? 0 :this.getIdCard().hashCode());
return result;
}
多态和抽象类
1. 多态
多态的实现方式:
1.父类作为形参 实参为子类类型
2.父类作为返回值 实际返回值类型为子类
2. 向上转型、向下转型
1.向上转型 Pet pet = new Dog();
此时可以调用子类重写父类的方法 和继承父类的方法 不能访问子类独有的方法
2.向下转型
向下转型是指 将 指向子类对象的父类引用 转换为子类类型
而不是直接将一个父类类型强制转换为子类
Dog dog = (Dog)pet;
此时可以调用子类独有的方法 以及继承父类、重写父类的方法
3. instanceof关键字
对象名 instanceof 类名 表示判断左侧的对象是否属于右侧的类型 返回值为布尔类型的
4. 抽象类
1.抽象方法没有方法体 必须存在于抽象类中 抽象方法和抽象类都必须使用abstract修饰
2.抽象类不能直接new对象 必须通过new子类对象的方式创建 多态向上转型的方式
3.子类必须重写父类的抽象方法 除非子类也是抽象类
4.抽象类中可以书写普通方法 普通属性 构造方法
5.可以书写静态方法 静态方法可以被继承 不能被重写
6.抽象类实现多态与之前一致
5. final关键字
5.1 final修饰常量
final修饰的属性 称之为常量
名字全部大写 多个单词下划线分割
通常在定义的时候赋值 或者在构造方法中赋值
5.2 final修饰方法
5.3 final修饰类
接口
1.接口中默认都是全局抽象方法 不管是否书写 public abstract 都有这个效果
2.接口不能直接new对象 必须通过new实现类(子类)的方式创建对象 多态向上转型
3.实现类必须重写接口中的所有抽象方法,除非实现类也是抽象类或者接口
4.接口中不能写普通属性 默认都是全局静态常量 不管是否书写public static final都有这个效果
5.接口中不能写构造方法
6.接口可以继承多个接口 实现类可以实现多个接口
7.接口依然可以实现多态 实现的方式与之前一致
面试题:Java支持多继承吗?
不支持,但是我们可以通过接口继承多个接口的方式 模拟多继承的效果
常用类
1. 枚举
枚举 enum修饰,用于规范某个数据的取值
枚举中默认都是全局静态常量的取值
直接写值,多个值使用逗号分割
2. 包装类
byte short int long float double boolean char
Byte Short Integer Long Float Double Boolean Character
包装类构造方法
除了char包装类之外,其他的包装类都支持传入一个与之对应的基本数据类型构造当前实例
char包装类只支持传入char类型
2.1 包装类-基本数据类型互相转换方法
xxxxValue() 将包装类转换为基本数据类型
valueOf() 将基本数据类型转换为包装类
除了Character类以外 其他的包装类 还支持传入一个String对象 转换类型
2.2 字符串与基本数据类型互相转换
toString方法 将基本数据类型转换为字符串
parseXXX() 除了char包装类 其他包装类都提供对应的将字符串转换为基本数据类型的方法
3. 拆箱和装箱
自动装箱和拆箱:JDK1.5以后允许包装类与基本数据类型混合运算
装箱:将基本数据类型转换为包装类 调用valueOf()
拆箱:将包装类转换为基本数据类型 调用xxxValue()
4. Math类
package com.qfedu.test5;
/**
* Math类 数学类
* 提供有常用的数学计算方法 和 两个静态常量 E PI
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
System.out.println(Math.E);
System.out.println(Math.PI);
// 以下方法面试题
System.out.println(Math.abs(-300)); //绝对值
System.out.println(Math.ceil(-3.3)); //向上取整
System.out.println(Math.floor(3.9)); //向下取整
System.out.println(Math.round(3.6)); //四舍五入
System.out.println(Math.round(35));
System.out.println((int)(Math.random() * 13)); //随机数
}
}
5. Random类
6. String类
对String对象进行操作时,会创建一个一个StringBuilder对象,初始化Stringbuilder对象后调用它的append方法,最后再tostring生成一个新的字符串,因为初始是一个字符串,结束也是一个字符串,所以可以说String是不可变的,也可以说String是可变的
字符串类提供的有很多操作字符串的方法
比较 equals()
忽略大小写比较 equalsIgnoreCase()
长度 length()
大小写 toUpperCase() toLowerCase()
截取字符串 substring() 包前不包后
查找字符串出现的位置 第一次 indexOf()
最后一次 lastIndexOf()
去除空格 trim() 去除的首尾的空白
拼接字符串 concat
拆分字符串 split()
判断字符串是否以某一个字符串开头或者结尾 startsWith() endsWith()
charAt(int index) 返回指定位置的字符
toCharArray() 将字符串转换为char数组
replace(String oldStr,String newStr) 替换指定内容的字符串
replaceAll(String regx,String newStr) 支持正则表达式的字符串替换
isEmpty() 判断是否为null 长度为0 返回为true 否则false
7. String类和包装类相关面试题
==和equals有什么区别?
String 类直接使用=号赋值 将先从常量池中查找有没有同内容的变量
如果有 则直接使用已存在内容的地址
如果没有 将此内容存在常量池 完成赋值
这样做的好处是为了节省内存空间
==和equals有什么区别?
整型包装类和char包装类直接使用=号赋值 取值范围在byte以内
数值相同 ==比较为true
超过byte取值范围 ==比较为false
因为JDK的开发人员为了节省内存空间 将byte取值范围内的数值存放在一个缓存数组中
如果在byte取值 范围 那么将从数组中取出对应的值
如果不再byte取值范围 直接new一个新的对象
8.StringBuffer&StringBuilder
String类是一个不可变对象 因为String类底层维护的是一个final修饰的char数组
任何对原字符串进行的增删改操作 都将产生一个新的字符串
所以 当我们需要频繁改变一个字符串的内容 推荐使用StringBuffer或者StringBuilder
面试题:
StringBuffer StringBuilder 区别?
StringBuffer是线程安全的 JDK1.0
StringBuilder线程不安全 JDK1.5
9. Calendar类
Calendar类 日历类 也提供了获取年月日时分秒方法
Calendar类不能new对象
package com.qfedu.test5;
import java.util.Calendar;
/**
* Calendar类 日历类 也提供了获取年月日时分秒方法
* Calendar类不能new对象
* @author WHD
*
*/
public class TestCalendar {
public static void main(String[] args) {
Calendar instance = Calendar.getInstance();
System.out.println(instance.get(Calendar.YEAR) + "年");
System.out.println(instance.get(Calendar.MONTH) + 1 + "月");
System.out.println(instance.get(Calendar.DAY_OF_MONTH) + "日");
System.out.println(instance.get(Calendar.HOUR) + "时");
System.out.println(instance.get(Calendar.HOUR_OF_DAY) + "时");
System.out.println(instance.get(Calendar.MINUTE) + "分");
System.out.println(instance.get(Calendar.SECOND) + "秒");
}
}
异常
1. 异常处理
1.1 try -catch
try 尝试 表示将可能出现异常的代码 存放在try中
try不能单独出现 必须结合catch 或者 finally
catch 捕获 表示捕获对应的异常 在catch代码块中处理
情况1:
捕获的是InputMismatchException 出现的也是InputMismatchException
可以捕获到异常 不会中断程序
情况2:出现的异常和捕获的异常不同 依然会中断程序
情况3:如果一段代码中有可能出现多个异常 我们可以使用多个catch块处理
情况4:
我们可以统一写一个异常父类 来处理所有的异常
异常catch块书写顺序 先子类 后父类
1.2 finally
finally 表示最终的意思
表示最终不管 是否出现异常 以及异常是否被捕获到 都将执行的代码
finally不能单独出现 必须结合try 或者 try -catch
finally不执行的唯一情况 :在finally执行之前退出JVM虚拟机
System.exit(int status); 状态码 0表示 正常退出 非0表示非正常退出
目前写哪个数值都可以退出 没有任何区别
以下代码 在finally中对返回值的操作 不会影响返回值
实际开发中 不推荐在finally中对返回值做操作
执行顺序:先执行try中return 再执行finally 最后再次执行try中的return
还使用最初确定的返回值
package com.qfedu.test3;
/**
* 以下代码 在finally中对返回值的操作 不会影响返回值
* 实际开发中 不推荐在finally中对返回值做操作
*
* 执行顺序:先执行try中return 再执行finally 最后再次执行try中的return
* 还使用最初确定的返回值
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
System.out.println(m1());
}
public static int m1() {
int num = 10;
try {
num ++;
return num;
} catch (Exception e) {
e.printStackTrace();
}finally {
num ++;
}
return num;
}
}
1.3 throw和throws
throw表示抛出异常
抛出的异常分为两大类:
1.检查异常CheckedException 调用者必须处理 要么try-catch 要么继续往后声明
2.运行时异常RuntimeException 调用者不是必须处理
throws表示声明异常
throw throws
生成并抛出异常 声明方法内抛出了异常
位于方法体内部,可作为单独语句使用 必须跟在方法参数列表后面,不能单独使用
抛出一个异常对象,且只能是一个 声明抛出异常类型,可以跟多个异常
2. 自定义异常
package com.qfedu.test5;
public class InputAgeException extends Exception{
private static final long serialVersionUID = -5819204170683788513L;
public InputAgeException(String message) {
super(message);
}
}
package com.qfedu.test5;
public class SexNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
// alt + s 选择从父类生成构造方法
public SexNotFoundException(String message) {
super(message);
}
}
集合
Collection接口
1. List接口
1.1 ArrayList
常用方法
package com.qfedu.test1;
import java.util.ArrayList;
import java.util.InputMismatchException;
/**
* Collection
* List
* ArrayList类常用方法
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(10);
list.add(3.5);
list.add('a');
list.add("hello");
list.add(false);
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(20);
list1.add(35);
list1.add(30);
list1.add(55);
list1.add(60);
System.out.println(list1.size());
System.out.println(list1.remove(0));
System.out.println(list1.size());
list1.set(0, 666);
System.out.println(list1.get(0));
System.out.println(list1.isEmpty());
list1.clear();
System.out.println(list1.isEmpty());
ArrayList<Student> list2 = new ArrayList<Student>();
Student stu1 = new Student("赵四", 20);
list2.add(stu1);
list2.add(new Student("赵四", 20));
list2.add(new Student("赵四", 20));
list2.add(new Student("赵四", 20));
list2.add(new Student("赵四", 20));
list2.add(new Student("赵四", 20));
list2.remove(stu1);
System.out.println(list2.size());
}
}
package com.qfedu.test2;
import java.util.ArrayList;
import java.util.Iterator;
/**
* ArrayList集合遍历方式
* 1.普通for循环
* 2.迭代器遍历
* 3.增强for循环
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 200000; i++) {
list.add(i);
}
// 方式1 普通for循环
long beginTime = System.currentTimeMillis();
for(int i = 0; i < list.size();i++) {
System.out.println(list.get(i));
}
long endTime = System.currentTimeMillis();
System.out.println("普通for循环耗时" + (endTime - beginTime));
// 方式2 迭代器遍历
beginTime = System.currentTimeMillis();
Iterator<Integer> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
endTime = System.currentTimeMillis();
System.out.println("迭代器耗时" + (endTime - beginTime));
// 方式3 增强for循环 底层实现依然还是迭代器 是JDK1.5新增的写法
beginTime = System.currentTimeMillis();
for(Integer i :list) {
System.out.println(i);
}
endTime = System.currentTimeMillis();
System.out.println("增强for循环耗时" + (endTime - beginTime));
}
}
数据结构
ArrayList集合特点和数据结构
底层实现是一个Object数组,有下标,有序,可以为null,可以重复,线程不安全
查询修改快,因为有下标
增删慢,因为需要移动元素
ArrayList集合源代码解读:
1.当我们调用无参构造 初始化一个初始为空的数组
2.当我们第一次添加元素的时候 将数组容量改为10
3.如果长度不够 将扩容为原来的1.5倍
1.2 linkedList
数据结构
LinkedList数据结构
双向链表,没有初始大小,不需要扩容,没有个数上限,有序(插入顺序),可以为null,可以重复,线程不安全
查询,修改慢,因为没有下标,我们根据某个序号查找元素必须先找到与之相邻的元素,以此类推
删除,添加块,因为不需要移动元素,只需要改变新的指针即可
1.3 Vector
ArrayList与Vector的区别?
ArrayList线程不安全 Vector线程安全
ArrayList初始为0 Vector初始为10
扩容1.5倍 扩容2倍
2. Set接口
Set接口存储一组唯一,无序的对象,不允许两个对象equals方法比较为true,并且hashCode相同
2.1 HashSet
HashSet 特点 :无序 不能重复 去除重复的原理 两个对象equals比较为true 并且hashCode相同
底层是有一个HashMap维护的
2.2 TreeSet
TreeSet底层维护的是一个TreeMap
有序的Set集合 比较的顺序 元素必须实现Comparable接口,重写compareTo方法
2.3 LinkedHashSet
Map接口
1. HashMap
HashMap:无序,键和值可以为null,键不能重复,线程不安全
常用方法
package com.qfedu.test6;
import java.util.HashMap;
/**
* Map接口
* HashMap常用方法
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String,String>();
map.put("CN", "中国");
map.put("US", "美国");
map.put("JP", "小日本");
map.put("KR", "棒子");
map.put("ID","阿三");
System.out.println(map.size());
System.out.println(map.remove("JP")); // 删除
System.out.println(map.size()); // 长度
map.put("CN", "中华人民共和国"); // 存放
System.out.println(map.get("CN")); // 获取
map.replace("US", "美国佬");
System.out.println(map.get("US"));
map.clear();
System.out.println( map.isEmpty());
}
}
遍历方式
package com.qfedu.test7;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
/**
* HashMap 遍历方式
* 1.获取所有的键
* 2.获取所有的值
* 3.获取所有的键的迭代器
* 4.获取所有的值的迭代器
* 5.获取所有的元素Entry数组
* 6.获取所有的元素的迭代器
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String,String>();
map.put("CN", "中国");
map.put("US", "美国");
map.put("JP", "小日本");
map.put("KR", "棒子");
map.put("ID","阿三");
// 1.获取所有的键
Set<String> keySet = map.keySet();
for(String key : keySet) {
System.out.println(key + "\t" + map.get(key) );
}
System.out.println("=====================================");
// 2. 获取所有的值
Collection<String> values = map.values();
for(String v : values) {
System.out.println(v);
}
System.out.println("=====================================");
// 3.获取键的迭代器
Iterator<String> iterator = map.keySet().iterator();
while(iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + "\t" + map.get(key));
}
System.out.println("=====================================");
// 4.获取所有的值的迭代器
Iterator<String> iterator2 = map.values().iterator();
while(iterator2.hasNext()) {
System.out.println(iterator2.next());
}
System.out.println("=====================================");
// 5.获取所有的键值对组合
Set<Entry<String, String>> entrySet = map.entrySet();
for(Entry<String,String> entry : entrySet) {
System.out.println(entry.getKey() + entry.getValue());
}
System.out.println("=====================================");
// 6.所有键值对组合的迭代器
Iterator<Entry<String, String>> iterator3 = map.entrySet().iterator();
while(iterator3.hasNext()) {
Entry<String, String> next = iterator3.next();
System.out.println(next.getKey() + next.getValue());
}
}
}
数据结构
HashMap数据结构
回顾我们之前学过的两种数据结构:
ArrayList基于数组的,因为有下标,所以查询、修改快,增删慢
LinkedList基于链表的,因为没有下标,所以查询、修改慢,增删快
以上两个数据结构是刚好互补的,如果能将两个数据结构结合在一起,完美~
这只是理想状态,实际情况不会这么完美,另外结合在一起比较浪费空间
HashMap就是将两种数据结构结合在了一起
JDK7 数组 + 单向链表
JDK8 数组 + 单向链表 + 红黑树
HashMap数据的存放过程:
HashMap中一个元素是一个Node(节点),一个节点包含四个部分:Key值,value值,根据key计算出来的hash值,下一个元素的引用next
当我们调用put方法往HashMap中存储数据,会先根据key的hash值找到当前元素应该存放在数组中的位置,
如果此位置没有元素,则直接存放,
如果此位置有元素,那么向下延伸为单向链表
如果链表的长度大于8并且元素的总个数超过64 那么单向链表转换为红黑树
在我们使用过程中,我们会删除元素,如果链表的长度小于6,将红黑树再次转换为链表
HashMap中两个重要的数值:
16 表示数组的长度为初始长度16
0.75 表示数组的使用率达到75% 就扩容 扩容两倍 resize() 方法
2. Hashtable
Hashtable JDK1.0 提供与HashMap相同的API 并且不允许null作为键或者值 线程安全
初始长度为11 负载因子0.75 扩容 rehash() 两倍+1
HashMap与Hashtable的区别?
HashMap线程不安全 Hashtable线程安全
HashMap允许null键和值 Hashtable不允许
HashMapJDK1.2 Hashtable JDK1.0
3. LinkedHashMap
LinkedHashMap是基于双向链表的有序的Map集合 顺序为插入顺序
4. TreeMap
基于树的Map集合 有序是按照键的比较顺序
使用自定义的类型作为TreeMap键 必须实现Comparable接口 重写compareTo方法
指定比较规则 否则运行报错 不能添加数据
5. Properties
Properties类用于存放成对的字符串数据 实际开发中用于读取配置文件
所以此类不要使用从父类Hashtable继承来的put或者putAll方法
而应该使用setProperty()
Collections
Collections和Collection的区别?
前者是工具类 后者是集合父接口
工具类提供的有操作集合一些方法 比如查找集合最大、最小元素,集合排序等
泛型
泛型 用于统一数据类型 并且扩展代码 相当于是一个占位符 表示未知的类型
- 泛型不能类型转换 不能多态
- List<类型A> 表示此list只能存放A类型的数据
- 泛型应用场景:
- 类、方法、形参、接口
- 泛型字母表示的含义 通常是单词首字母
- T Type 类型
- E Element 元素
- P Parameter 参数
- R Return 返回值
- K Key 键
- V Value 值
- 以上字母是业界公认有特定含义的简写 可以任意写字母 也可以小写 但是不要太随意
多线程
1.进程
进行中的应用程序,只有一个应用程序处于运行状态,才能被称之为进程。
应用程序的执行实例,有独立的内存空间和系统资源。2. 线程
CPU执行的最小单位,包含在进程之中。
CPU调度和分派的基本单位,应用程序运算是最小单位。3. 进程和线程的关系
进程和线程的关系,就像车身和车轮的关系,不是越多越好,要根据实际的硬件环境,选择最合适的数量,才是最优方案。4. CPU和线程的执行
单核心CPU下,多线程是轮流交替执行。每个任务最多执行20ms,每个任务准备好以后,会向CPU发出指令:准备好了 真正是否能够被执行 不确定 因为同时可能有很多线程都准备好了5. 并发和并行
并发是指同时发生,轮流交替来执行
并行是真正意义 上的同时执行6. 线程的创建
6.1 继承Thread类
线程的创建方式1:继承Thread类 重写run方法 ```java package com.qfedu.test2; /**- 线程的创建方式1:继承Thread类 重写run方法
- @author WHD
/
public class T1 extends Thread{
@Override
public void run() {
}System.out.println(Thread.currentThread().getName());
public static void main(String[] args) {
T1 t1 = new T1();
t1.setName("线程A");
t1.start(); // 调用start方法才会开启新的线程
// t1.run(); 调用run方法不会开启新的线程 依然使用main线程调用run方法 }
}
<a name="x2q8W"></a>
#### 6.2 实现Runnable接口
创建线程方式2 :实现Runnable接口 重写run方法
```java
package com.qfedu.test2;
/**
* 创建线程方式2 :实现Runnable接口 重写run方法
* @author WHD
*
*/
public class T2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
T2 t2 = new T2();
Thread thread = new Thread(t2, "线程A");
thread.start();
}
}
6.3 两种方式对比
继承Thread类
编写简单,可直接操作线程
适用于单继承
实现Runnable接口
避免单继承局限性
便于共享资源
推荐使用实现Runnable接口方式创建线程
7. 线程的状态
8. 线程的优先级
线程的优先级 默认为5 最低为1 最高为10
优先级高代表获取CPU的概率较大 并不能保证一定会优先获得CPU资源
MAX_PRIORITY 最高10
MIN_PRIORITY 最低1
NORM_PRIORITY 默认5
9. 线程的休眠
10. 线程的插队
线程的插队
join() 等待插队线程执行完毕 再执行当前线程
join(long mills) 等待插队线程固定时间 时间结束就执行当前线程
11. 线程的礼让
yield()方法 线程礼让 当前线程礼让其他线程 让其他线程先执行
只是提供一种可能 不一定会礼让
12.线程的中断
interrupt() 中断线程方法 但不是立即中断 如果当前线程有未执行完的任务 将执行完毕再中断
interrupted() 查看当前线程是否可以被中断 true为是 false为否
stop() 为立即中断线程的方法
13.synchronized关键字规则
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码