复习
1、安装JDK,配置环境变量
当前使用JDK1.8版本作为我们学习的jdk版本,安装JDK1.8。可以不安装单独的jre,因为jdk中包含一个jre。
配置环境变量:1、配置系统变量
需要新建JAVA_HOME变量,配置JAVA_HOME,这个JAVA_HOME的值就是jdk的安装根目录。
path是系统原来就有的变量,需要在path上追加JAVA_HOME:
在整个path的最前面,配置:%JAVA_HOME%\bin.
2、第一个JAVA程序
1、创建java源文件,新建一个后缀名是.java的文件,这个就是java源文件。
2、在源文件中编写java类,建议一个java文件中只有一个java类,这个java类是public修饰的,当一个class被public修饰,那么这个class的类名必须和文件名同名。一个java源文件中,其实可以有多个class,但是只有一个class被public修饰,但是不建议在一个java文件中写多个class。
public class HelloWorld{
public static void main(String[] args){
System.out.println("HelloWorld!");
}
}
3、使用javac命令编译源文件:
javac HelloWorld.java
4、编译完成之后,会出现一个.class字节码文件,使用java命令运行这个文件,没有后缀名
java HelloWorld
3、在程序中声明和使用变量
1、变量:变量是一个容器,这个容器用于装在一个数据。
2、变量的三要素:变量的数据类型,变量名,变量当前值。
3、声明和使用变量的流程:
3.1:变量必须先声明再使用。
3.2:声明变量必须给出变量类型。
3.3:变量必须有值才能使用。
4、变量的数据类型
Java语言中有八种基础数据类型
byte 字节 一个字节8位
short 短整型 两个字节16位
char 字符 两个字节16位
int 整型 四个字节32位
long 长整形 八个字节64位 一个整数默认是int型,需要在一个整数后加l或L表示这个整数是long型
float 单精度浮点数 四个字节32位 默认情况下浮点数是double,所以直接用一个浮点数不能给float赋值,所以需要在浮点数后面加一个f或F来表示这个浮点数是一个float。
double 双精度浮点数 八个字节64位
boolean 布尔型 只有两个值true和false
5、java语言中的运算符
算术运算符
+,-,*,/,%
+,-,*这三个运算符和数学中没有什么不同。
/和%需要看数据类型,如果是浮点型,就只有/,结果会包含小数点,没有%。如果是整数,那么/就是商也叫取整,%就是余数也叫取余。
优先级是乘除大于加减。
++,–:++自增1 --自减1。分前++和后++。前++就是先自加在运算,后++就是先运算后自加。
int i = 10;
int j = ++i;i=11,j=11
int j = i++;i=11,j=10
赋值运算符
= 右侧的值给左侧的变量
右侧一共会出现四种情况:常量赋值,表达式运算结果赋值,变量值赋值(值传递),方法的返回值赋值
+= -= *= /= %=
i += j:等价于 i = i + j;
关系运算符(比较运算符)
< > <= >= == !=关系运算符一共6个,6个运算符平级。
关系运算符连接的叫关系表达式,关系表达式的结果是boolean。关系运算符是二元运算符,运算符的两端必须也只能各有一个变量
逻辑运算符
& | ^ ! && ||
逻辑运算符的作用是连接关系表达式。
&&的两端必须都是true结果才返回true,只要有一段是false就会返回false
比如:num >= 50 && num <= 100
||的两端只要有一端是true就返回true,两端同时为false才会返回false
比如:gender==‘男’ || gender==‘女’
位运算符
& | ^ ~ << >> >>>
位运算符只能进行整数的运算,位运算符是按照操作数的二进制补码的形式进行运算的。
6、if-else和多重if-else
1、单独的if结构
if(关系表达式(true/false)){
// 当表达式的结果是true的时候,将执行if后面大括号的代码
}
2、if-else结构
if(关系表达式(true/false)){
// 当表达式的结果是true的时候,将执行if后面大括号的代码
}else{
// 当表达式的结果是false的时候,将执行if后面大括号的代码
}
3、多重的if-else
if(关系表达式(true/false)){
// 当表达式的结果是true的时候,将执行if后面大括号的代码
}else if(关系表达式(true/false)){
}
…(else-if可以有任意多个)
else{// (可选)
// 当表达式的结果是false的时候,将执行if后面大括号的代码
}
从第一个判断条件开始依次判断,当一个条件判断为true的时候,则执行对应条件的代码段,执行完毕退出整个if-lese结构。所以,只会有一个if或者else-if块会被执行。
7、switch-case
1、格式:
switch(变量){
case 常量1:
break;
case 常量2:
break;
case 常量3:
break;
…
default:
}
2、运行过程:
用switch后面的变量的值去匹配常量值,从匹配的常量值得case开始顺序向下执行,直到switch结束。
所以为,为了避免贯穿case,要使用break。
3、switch后面的变量可以是下列类型:
byte char short int 枚举 String
8、循环
while循环:
使用场合:次数不确定,先判断后执行
while(循环条件){
循环操作
}
特点:当循环条件为true的时候,会进入循环体执行循环操作。要保证循环可以退出。由于先判断后执行,那么有可能循环初始条件不满足循环条件为true,如果这样,则while循环一次都不会执行。
do-while循环:
使用场合:次数不确定,先执行后判断
do{
循环操作
}while(循环条件);
特点:do-while循环会首先执行一次do的循环操作,然后判断循环条件,循环条件为true,则继续循环执行do的循环操作,如果循环条件为false,则退出循环。
for循环:
使用场合:次数确定,先判断后执行。
for(int i=0;i<=100;i++){
循环体;
}
特点:for后面的小括号中由两个分号分割成三个部分,第一个部分循环变量的定义,第二部分是循环条件,当循环条件为true进入循环体,当循环条件为false的时候,退出循环。第三个部分是循环变量的改变。这三个部分都可以省略,但是两个分号不能省略
9、break和continue关键字
break:提前结束循环(跳出循环),当程序执行到break关键字的时候,循环会立刻终止,程序从循环下继续执行。
break除了可以控制循环,还可以控制switch结构,注意就近原则,break会控制离它最近的那个它能控制的结构。如果循环中嵌套了switch,而break又写在switch里,那么这个break控制的是switch而不是循环。
continue:只能控制循环,但也有就近原则。当程序执行到continue关键字的时候,是结束本次循环,开始下一次循环,程序会回到循环起点,再次判断循环条件。
由于break关键字的存在,可以使得死循环也作为正常的结构被使用。
10、二重循环
概念:当一个循环完整的包裹在另一个循环的循环体中,就叫二重循环(嵌套的循环)。
特点:外层循环循环一次,内层循环需要执行完所有的循环次数。
for(){
for(){
}
}
经典的二重循环题:九九乘法表,打印图形,冒泡排序。
11、数组
概念:保存多个数据的容器。
数组有四要素:数组名,数组类型(数组中只能存放同种类型的数值),数组长度,数组元素(元素相当于同种类型的变量,访问数组元素的方式是数组名[下标],下标的合法区间0-数组长度-1)
数组的声明和使用:
声明:三种方式:数组必须在声明的时候给出长度,并且这个长度将不再改变
1、int[] nums = new int[5];// 声明了数组的长度和分配了数组的空间,但是没有为数组元素赋值。元素将使用默认初值。
java语言中允许第二种写法:int nums[] = new int[5];
2、int[] nums = {3,56,80,100};// 声明的同时为每个数组元素赋值,长度就是数组元素的个数。必须一行之内完成。
3、int[] nums = new int[]{12,50,30};// 声明的同时为每个数组元素赋值,长度就是数组元素的个数。允许断成两部完成。
二维数组:一维数组的数组元素是另一个数组
二维数组的声明:
动态声明:
int[][] nums = new int[5][3];// 同时给出一维和二维长度
int[][] nums = new int[5][];// 声明是只给出一维长度,二维长度不给出,这种声明方式,必须先给出二维数组的长度,初始化二维数组才能使用。
静态声明:
int[][] nums = {{1,2,3},{4,5,6},{7,8,9}};// 一维和二维数组全静态声明
int[][] nums = new int[5][];
nums[0] = new int[]{1,2,3};// 静态声明二维数组
nums[1] = new int[]{4,5,6};
nums[2] = new int[]{7,8,9};
二维数组的遍历:使用二重循环遍历二维数组
int[][] nums = {{1,2,3},{4,5,6},{7,8,9}};
for(int i=0;i<nums.length;i++){// 外层循环遍历一维数组
for(int j=0;j<nums[i].length;j++){// 内层循环遍历二维数组
System.out.println(nums[i][j]);// 访问数组元素
}
}
12、类和对象
1、包:管理java源文件的一种结构,在硬盘上包就是文件夹。包中可以包含java类也可以包含包。
当一个java类处于某一个包中时,需要在这个java文件的第一行使用package关键字类定义这个类属于的包。
package com.morning.test
public class Student{
public String stuName;
public void sayHello(){
}
}
2、类和对象:
面向对象编程:遇到问题的时候首先考虑哪个类中有对应的方法可以解决问题,然后创建这类的对象,由对象去调用方法解决问题。
类:模板,规范,对象的类型,规定了一类对象必须有的属性和方法。
对象:对象是类的实例,对象调用属性和方法,对象是由类创建的。
定义类:定义一个类应该有的属性和方法。类的属性可以是基础类型,类的属性也可以是引用类型。
类的方法:定义方法的时候需要定义一个方法的五个部分:访问修饰符,返回类型,方法名,参数组,方法体。
13、方法
1、方法的组成部分:一个方法由五个常规部分组成:访问修饰符,返回类型,方法名,参数组,方法体。
其中方法的返回类型,方法名,参数组这三个部分最重要。
返回类型:一个方法如果规定了返回类型,那么必须在方法结束前返回一个该类型的变量。使用return关键字。如果方法不需要返回值,那么返回类型的位置要写void。方法的返回值返回到方法的调用点。一个java的方法只能返回一个值。
return:1、在方法结束的时候返回一个值。
2、提前结束方法。当return关键字执行的时候,方法就会结束。return关键字后面的代码将不再执行。
参数组:Java语言规定,一个方法可以声明无数多个参数,每个参数用逗号隔开。声明参数的时候参数有类型,这个时候叫做形参。
public void methodOne(int one,int two,String opert){
}
当需要调用一个带参的方法的时候:一定要按照方法的的要求传参。方法规定几个参数就需要传递几个参数,方法规定参数是什么类型,就需要传递什么类型,方法规定什么参数顺序,就需要按照顺序传参。传递给方法调用的参数叫做实参,此时只有参数名不需要参数类型。
int one = 100;
int two = 200;
String opert = "+";
methodOne(one,two,opert);
参数分成两类:
1、基础类型参数:参数的类型为八种基础数据类型,此时参数只有值,没有其他。这种传递就叫值传递。这种时候,如果方法里对实参进行了修改,那么这个修改不会影响调用点的实参。
2、引用类型参数:参数的类型为对象类型,此时参数有属性和方法。此时传参叫引用传递。这种时候,如果方法里对实参进行过修改,那么这个修改会影响调用点的实参。
变长参数组:定义:在方法的参数列表的最后一个,使用类型… 参数名的格式声明一个变长参数组。
public void methodOne(int... nums){
for(int i=0;i<nums.length;i++){
nums[i] = 10;// 变长参数组的参数实际上和数组元素是一样的。
}
}
传参:调用变长参数组方法的时候,按照参数之间使用逗号分隔的方式,传递多少个参数,那么数组中就会有多少个元素。
methodOne(10,20,30,40)
规定:1、一个方法只能有一个变长参数组参数。2、这个变长参数组必须在参数列表的末尾。
14、构造方法
1、长相特殊:没有返回类型,方法名跟类同名。
调用特殊:只能在创建对象时调用,且仅能调用这一次。
作用:在创建对象的时候进行初始化操作。
2、构造方法的重载:
2.1如果一个类中没有任何一个显示的构造方法,那么将默认配备一个隐示的无参的构造方法。
2.2如果写了任何一个显示的构造方法,那么这个默认隐示无参的构造方法就消失了。但是请写出来这个无参的构造方法。
2.3 一个类中有多个参数组不同的构造方法,根据需求,可以任意调用任何一个最符合当前情况的构造方法创建对象。
15、方法重载
概念:在同一个类中,方法名相同,参数组不同(参数类型不一致,参数个数不一致,参数顺序不一致)的多个方法,构成方法重载。
意义:可以简化调用点的工作。对于调用点来说,只需要记一个方法名,根据参数的不同,编译器会找到对应参数的方法执行。
public void methodOne(int one){
}
public void methodOne(String word){
}
public void methodOne(int one,String word){
}
public void methodOne(String word,int one){
}
16、继承
概念:如果类A extends 类B,那么就叫做A继承自B,A被称作子类,B被称作父类。子类可以继承到父类所有的属性和方法,构造方法不能继承。
public class A extends B{// 子类
}
public class B{// 父类
}
Java的单继承:Java语言规定,一个类只能有一个父类。
Object类:如果一个类没有显示的继承自另一个类,那么它将默认继承自Object类。Object类是所有java类的总父类。
17、多态
概念:任何一个需要父类引用的位置都可以传递一个子类对象。
实现步骤:1、有继承,父子类
2、父类定义方法,子类重写方法
3、父类的引用,子类的对象。
表现形式:
1、创建对象多态
Teacher teacher = new LangTeacher();// 等号左侧为编译时类型,父类。等号右侧为运行时类型,子类。
2、传参多态
public class Master{
public void sendTeacher(Teacher teacher){// 定义方法参数的时候,类型是父类类型
teacher.jiangKe();
}
public static void main(String[] args){
Master master = new Master();
LangTeacher lt = new LangTeacher();// 准备参数时,创建子类对象。
master.sendTeacher(lt);// 传递参数时,传递子类对象。
}
}
向上转型和向下转型:
1、向上转型:
Teacher teacher = new LangTeacher();// 多态表现形式就是向上转型。
2、向下转型:
LangTeacher lt = (LangTeacher)teacher;// 将一个父类引用类型的对象强制转换成子类引用类型
注意:向下转型就是使用强制类型转换,这种时候,如果转换类型和实际对象类型不匹配,则会爆出类型转换异常。一定要保证这个对象本身是什么类型才能转成什么类型。
instanceof关键字:用于判定某个对象是不是这个类的对象。关键字的左侧一定是一个对象,关键字的右侧一定是一个类型。如果这个对象是这个类型的对象,则表达式返回true,如果不是,则表达式返回false
if(lt instanceof LangTeacher){
// 是语文老师的对象
}else{
// 不是语文老师的对象
}
18、抽象类:
1、概念:被abstract关键字修饰的就是抽象类。
public abstract class Animal{
}
2、特点:
1、不能被实例化:不能new对象。
2、抽象类中,可以有抽象的方法,也可以有普通的方法。
3、抽象类一般在多态中担任父类的角色。
19、抽象的方法
1、概念:使用abstract关键字修饰方法,并且方法没有方法体。
public abstract void eat();
2、特点:
1、抽象的方法必须在抽象的类里。
2、抽象的方法没有方法体,所以只能调用方法,不能运行方法。
3、抽象的方法必须等待子类重写之后,才会有方法体,才能运行。
4、当一个子类继承自抽象父类的时候,必须重写父类所有的抽象方法。
20、包装类
1、概念:Java语言为每一种基础数据类型都准备了对应的包装类,这个包装类可以将基础数据类型转换成引用数据类型。
2、常用方法:
1、基础类型和包装类型间的转换:
Integer i = new Integer(10);// 创建Integer对象,将基础类型作为参数传入
int x = i.intValue();// 调用intValue()方法讲一个引用类型转成基础类型。
从JDK1.5开始有自动装箱和自动拆箱。
2、包装类型和String间的转换
Integer i = new Integer("100");// 创建Integer对象时,将一个String作为参数传入。
String s = i.toString();// 调用Integer对象的toString()方法将Integer对象转成String。
21、接口
1、概念:使用interface关键字创建的结构被称为接口
public interface Fly{
}
2、特点:
2.1、所有的属性都是默认 public static fianl的修饰。
2.2、所有的方法都是默认 public abstract的修饰
2.3、接口不能被实例化。
3、使用特点:
3.1:为接口配备实现类:类使用implements关键字实现接口
public class Bird implements Fly{
}
要求:一个类实现接口,那么必须实现这个接口中所有的方法。
3.2:编译类型使用接口,创建实现类对象:
Fly fly = new Bird();
3.3 实现接口的特点:
1、一个类实现接口的时候不影响其继承别的类,同时java语言允许一个类实现多个接口。
2、一个接口有多个实现类:多态。
3、一个类实现多个接口:这个类实现的这些接口中,有任何一个接口需要对象都可以传递当前类的对象。
3.4:从JDK1.8开始接口中允许声明带有方法体的方法。
default方法:
public default void methodOne(){
}
default方法的调用需要创建接口实现类的对象,然后由对象.的方法调用这种default方法。
static方法:
public static void methodTwo(){
}
static方法可以由接口名直接调用。
接口中声明有方法体的方法破坏了接口的原则和之前制定的规范。所以不建议使用。
22、枚举
1、JDK1.5之前:JDK1.5之前没有枚举类,做枚举是程序员自己的行为。
1.1:实现步骤:1.构造方法私有。2.提供公开的该类属性以便外界访问。
public class Gender{
public static final Gender MAN = new Gender();// 性别男
public static final Gender WOMEN = new Gender();// 性别女
private Gender(){// 构造方法私有,禁止外界new该类对象
}
}
class Student{
private String stuName = "Tom";
private int stuAge = 20;
private Gender stuGender = Gender.MAN;// 使用枚举值给枚举类型的属性赋值
}
2、JDK1.5之后的枚举:提供了一个枚举类Enum,提供了一个关键enum
创建枚举类的时候使用enum关键字声明类
public enum Season{// 使用enum关键字声明的枚举类自动继承自Enum类,toString由Enum类已经写完了。
// enum声明的枚举类必须在整个类的一个部分写公开静态常量的枚举值。
// 省略public static final Season,省略等号右侧new
// 如果构造方法无参,直接写枚举值得名字
// SPRING,SUMMER,AUTUMN,WRITER;
// 如果构造方法有参,则直接在枚举值名字后面加小括号传参
SPRING("春暖花开"),SUMMER("夏日炎炎"),AUTUMN("秋高气爽"),WRITER("白雪皑皑");
private String info;
private Season(){
}
private Season(String info){
this.info = info;
}
}
3、Enum类中常用的方法:
public static void main(String[] args){
toString();// Enum类重写了toString(),返回该枚举类的枚举值的名字,这个值在Enum类中叫name
name();// Enum类中有一个属性叫name,这个name就是该枚举值的名字,name()返回的就是这个name
ordinal();// 该方法返回这个枚举值在枚举类中的编号,这个编号从0开始。
values();// 这是一个静态方法,该方法返回一个枚举数组,将该枚举类中所有的枚举值装在到一个数组中返回。
valueOf();// 这是一个静态方法,该方法吸收一个String型的参数,将这个String代表的枚举类对象返回。要求这个String必须是枚举的name,如果放进去的字符串不是name则会抛出异常。
}
23、内部类
1、概念:一个类的定义完全的包裹在另一个类的类体里。这个类叫做内部类
public class A{// 外部类
public class B{// 内部类
}
}
2、分类:成员内部类,静态内部类,局部内部类
成员内部类:在外部类类体中声明,没有static修饰的内部类。成员内部类对象必须先创建外部类对象再创建内部类对象。
public class A{// 外部类
public class B{// 内部类
}
}
class Test{
public static void main(String[] args){
A a = new A();// 创建外部类对象
A.B b = a.new B();// 使用外部类对象创建内部类对象
A.B bb = new A().new B();// 一句完成先创建外部类对象在创建内部类对象。
}
}
静态内部类:静态内部类可以直接创建对象,无需依赖外部类对象创建。
public class A{// 外部类
public static class B{// 内部类
}
}
class Test{
public static void main(String[] args){
A.B b = new A.B();// 直接创建内部类的对象
}
}
注意:普通内部类中不允许声明static的成员
局部内部类:声明在外部类的某个方法中的内部类。
public class A{// 外部类
public void methodOne(){
class B{// 局部内部类
}
B b = new B();// 在外部类类体中内部类的类名可以直接访问不需要加外部类类名,局部内部类只能在定义这个内部类的方法中访问,出了这个方法就无法访问了。
}
}
匿名内部类:是一种特殊的局部内部类。
public class Test{
public static void main(String[] args){
Fly fly = new Fly(){// 使用匿名内部类实现接口
public void flying(){
System.out.println();
}
};
}
}
interface Flay{
public void flying();
}
24、注解
概念:写在程序中,起到提醒和注释的作用,但是注解不会改变程序的流程和运行结果。
内置注解:
1、@SuppressWarnings(“unused”)抑制警告注解,参数就是抑制的警告类型。
2、@Deprecated标注方法过期注解。
3、@Override判定方法重写注解。
自定义注解:
public @interface MyAnno{
String value() default "hello";// 长得像方法的属性,如果这个属性的名字叫value那么可以省略属性名直接写属性值。
}
元注解:修饰注解的注解
@Target:标注注解可以修饰的结构范围。
@Retention:标注注解的声明周期,什么时间抛弃这个注解。SOURCE:编译期抛弃注解。CLASS:创建对象期抛弃。RUNTIME:运行期注解,没有抛弃。通过反射读取的只有RUNTIME注解。
25、异常
概念:异常也叫运行时错误,这类错误在编译期没有报错,但是在程序运行的时候会抛出错误。
特点:Java语言会在出现异常的那行代码处中止,创建该类异常对象,向上抛出。一旦这个异常对象被抛出到虚拟机中,则虚拟机会立即终止当前进程。
异常分类:
1、已检查异常:RuntimeException及其子类以外的异常类,系统强制必须处理,try-catch处理,方法throws处理。
2、未检查异常:RuntimeException及其子类,这类异常不强制必须处理,但是一旦出现异常还会走标准得异常处理流程。
Exception类:所以异常类的总父类。自定义异常也是继承自Exception类的异常。
异常处理:两种方式。
try-catch-finally方式:
构成:try块不能单独出现,必须和catch块或者和finally块一起出现。try块负责监控可能出现异常的代码,catch块负责捕获异常。finally负责释放资源(finally块无论出现异常与否都会执行)。
多重catch块:
try{
}catch(NullPointerException ex){// 针对每种独特的异常专门处理的catch块
}catch(InputMismathException ex){// 多重catch块会从上到下判断异常类,当匹配某一个异常类之后,就会执行catch块,然后绕过剩下的所有catch块,执行后面的代码。
}catch(Exception ex){// 多重catch块的末尾使用Exception,可以保证所有的异常都可以被捕获,另外不能将Exception写在上面的catch中,那样会导致下面的catch块永远都不会执行。
}
方法throws异常
public void methodOne()thrwos Exception{// 方法声明自己抛出何种异常,那么当该方法中有这类异常时,系统不会强制必须处理。方法抛出的异常交给方法的调用点来处理。
}
26、多线程
1、程序,进程,线程:
程序:为了完成某个任务或者某几个任务编写的一段代码。
进程:一个程序运行起来了。一个程序就是一个进程。
线程:在一个进程下,独立执行的一段代码。一个进程下可以有多个线程。
2、在Java程序中如何开启一个新的线程。
第一种方式:创建线程类继承Thread类。重写run()方法。run()方法中的代码就是需要开启新线程运行的代码。直接start()方法开启新线程。
第二种方式:创建线程类实现Runnable接口。实现run()方法。需要创建一个新的Thread类对象,将当前实现接口的类的对象作为参数传入,由线程类调用start()方法。
3、线程中的常用方法:
run():希望新线程运行的代码需要写在run()方法中。
start():开启新的线程,运行run()方法。
getName()/setName():设置和获取当前线程的名字。
getPriority()/setPriority():设置和获取当前线程的优先级,这个优先级从1-10,1最低10最高,默认情况下是5。
join():让另外一个线程加入到当前线程中运行,那么当前线程会中止,等待加入线程完全执行完毕当前线程继续执行。
sleep():让当前线程休眠,sleep方法可以接受一个int型的参数,这个参数是休眠的时长,单位毫秒。sleep会让当前线程处于中止状态,
当休眠的时长到达参数要求时线程会唤醒,当前线程继续处于争抢cpu的状态。当前线程会从sleep的休眠点开始继续执行。
wait():让当前线程处于等待状态,等待的线程会释放cpu,处于中止状态。wait的线程需要另外一个线程的notify方法来唤醒。
notify():随机唤醒另外一个处于等待的线程。
notifyAll():唤醒所有处于等待的线程。
4、线程安全问题:
出现的场景:多个线程争抢同一个资源的时候会出现线程安全问题。
解决方案:使用线程锁机制来解决线程安全问题。锁可以是任何一个类的对象。必须要保证争抢资源的多个线程使用的是同一个锁对象。
代码:synchronized代码块,将多个线程证券资源的代码写在代码块中,那么当一个线程执行这段代码时必须拿到锁对象。如果没有锁对象则不能进行代码。当一个线程拿到锁,其他线程拿不到锁,就不能干扰当前线程执行代码。资源就不会被多个线程同时作用。
synchronized("lock"){
// 需要加锁的访问统一资源的代码。
}
synchronized方法:
public synchronized void methodOne(){
// 整个方法里的代码都加锁,如果是一个普通方法,这个锁对象就是this,如果是一个static方法这个锁对象就是当前类的类类对象。
}
5、线程间的通讯
wait(),notify(),notifyAll():这三个方法都是属于Object。这个三个方法必须在同步代码块中使用。同步代码快的锁对象来调用这三个方法。
wait():让当前线程处于等待状态。处于等待的线程必须由其他线程唤醒。
notify():随机唤醒一个处于等待的线程。
notifyAll():唤醒所有处于等待的线程。
27、API中的常用类
1、Math数学类:拥有和数学运算有关的一系列方法。
Math.random():随机产生一个0到1之间的Double型浮点数。
2、API中的日期处理:
jdk1.8之前:Date,SimpleDateFormat,Calendar。
jdk1.8之后:LocalDate,LocalTime,LocalDateTime。
计算机中存储时间的格式:一个long型的变量。从1970年1月1日0时0分0秒开始到现在经历的毫秒数。
计算机中存储日期时,小时一般都是用24时制。月份都是0-11。星期0-6。小时0-23,分钟和秒钟0-59.
一个时间格式有七个部分组成:年,月,日,时,分,秒,毫秒。
28、数组的常见算法:
1、数组的反转
2、数组元素的查找:遍历每个元素进行查找。二分查找。
3、数组的扩容:+1扩容,倍数扩容。
4、数组元素的新增:追加式新增,插队式新增。
5、数组元素的删除。
29、String类和它的常用方法
1、String的创建对象的方法:
String s1 = "Tom";// 直接将常量池中的String对象地址赋值给引用
String s2 = new String("Tom");// 首先在常量池中有String对象,然后在堆中也会new对象,然后引用指向堆中对象,堆中的对象有个属性叫value指向常量池中的String对象。
2、String的不可变性:一个字符串对象一旦在声明的时候确定了串值,那么这个值将不再改变。由于字符串不可变,那么在拼接字符串的时候就会产生大量的对象。
3、String的常用方法:
String s1 = "nihao,";
s1 = s1.concat("chilema");//concat方法是拼接不是追加,所以拼接后会产生新的字符串,需要给引用重新赋值。
int index = s1.indexOf("ha");// 在调用方法的字符串中,从索引0开始查找第一次出现参数字符串的索引位置,找不到返回-1.
int index = s1.indexOf("ha",3);// 在调用方法的字符串中,从第二个参数所以位置开始向后查找第一次出现参数字符串的索引位置,找不到返回-1.
int index = s1.indexOf(97);// 参数还可以是int型字符的编码值。
int index = s1.indexOf('a');// 参数还可以是int型字符的编码值。
int index = s1.lastIndexOf("ha");// 在调用方法字符串中,从下标0开始查找最后一次出现参数字符串的索引位置,找不到返回-1.
int index = s1.length();// 返回字符串中的字符数,包含中间和两端的空格。
boolean flag = s1.equals("Hello");// 比较两个字符串的字符值是否相等,区分大小写。
boolean flag = s1.equalsIgnoreCase("Hello");// 比较两个字符串值是否相等,不区分大小写。
String s = s1.toLowerCase();// 将调用方法的字符串全部转成小写字母。
String s = s1.toUpperCase();// 将调动方法的字符串全部转成大写字母。
String s = s1.substring(int beginIndex);// 从参数索引位置开始截取到字符串末尾。
String s = s1.substring(int beginIndex,int endIndex);// 从第一个参数索引位置开始截取到第二个参数位置的前一位。
char[] chars = s1.toCharArray();// 将字符串转成一个char型数组。
byte[] bytes = s1.getBytes();// 将字符串转成一个字节数组。
String[] strs = s1.spilt("");// 将一个String按照参数的正则表达式要求转成String的数组。
String s = s1.replace("","");// 将调用方法的String按照第一个参数的正则表达式全部转成第二个参数的正则表达式内容。
30、集合框架
概念:集合是变长的,可以装载多个对象的容器。
组成:Collection,Map,Collection是单对象元素集合,Map是键值对元素集合。Collection接口有两个重要的子接口List和Set。
Collection接口的重要方法:
add(Object obj);// 添加一个元素
addAll(Collection coll);// 添加一个集合
remove(Object obj);// 移除一个元素
removeAll(Collection coll);// 移除一个集合中的所有元素
size();// 当前集合中的元素数。
clear();// 清楚集合中的所有元素
isEmpty();// 判定当前集合是否为空
iterator();// 返回一个迭代当前集合的迭代器
Collection接口的遍历:
// 第一种方式迭代器遍历
Iterator iter = coll.iterator();// 得到迭代器对象
while(iter.hasNext()){
Object obj = iter.next();// next()方法返回的一定是一个Object对象
String s = (String)obj;
}
// 增强for循环遍历
for(Object obj : coll){
Sting s = (String)obj;
}
31、List接口及其重要实现类
1、List接口的特点和重要方法:
特点:有序(插入顺序是遍历顺序),允许元素重复
重要方法:
add(Object o);// 向集合中追加一个元素。
add(Object o,int index);// 向指定所以位置插入一个新元素
addAll(Collection col);// 向集合中追加一个Collection集合的所有元素。
addAll(Collection col,int index);// 向指定索引位置插入一个Collection集合的所有元素。
Object o = get(int index);// 使用索引获取单个元素。
Iterator iter = iterator();// 获取迭代器对象。
remove(Object obj);// 移除参数元素。
remove(int index);// 移除下标位置的元素。
set(int index,Object obj);// 替换指定索引位置的元素
size();// 返回当前集合中的元素数
2、List接口的重要实现类:ArrayList类
特点:底层使用数组实现。遍历效率高,存取效率低。从jdk1.8开始,ArrayList创建对象的时候数组的长度是0,第一个添加元素的时候数组会初始化长度10(jdk1.8之前在创建ArrayList对象的时候数组就初始化长度10).当添加的元素数超过数组长度的时候扩容,按照数组原长度的1.5倍扩容。
3、List接口的重要实现类:LinkedList类
特点:底层使用双向链表实现,存储元素的对象是一个内部类的对象,这个对象包含item属性,prev属性,next属性。首元素prev是null,末元素next是null,其他元素perv和next指向同类对象,形成链表。item保存集合元素。遍历的效率低,存取的效率高,并且拥有一套对首末元素单独操作的方法。
addFirst(Object o);
addLast(Object o);
getFirst();
getLast();
removeFirst();
removeLast();
4、List接口的重要实现类:Vector
特点:在不考虑线程安全的情况下,优先选择使用ArrayList。除线程安全外,Vector其他方面和ArrayList一样。Vector底层使用同步锁方法保证对元素操作的方法线程安全。
32、Set及其重要的实现类
Set接口的特点:不允许元素重复,无序(插入顺序不是遍历顺序)
重要方法:同Collection
重要实现类:
HashSet:底层使用数组存储数据。新元素加入的时候使用hash值计算在数组中的索引,将元素存放到指定索引的数组位置。底层数组元素是链表,因此当两个元素的hash值计算的索引相同的时候。需要调用两个首元素的equals方法判定是否相等,如果相等则拒绝执行添加动作。如果判定不相等,则将新元素链接到原元素的next属性上。
TreeSet:底层使用红黑树存储数据,红黑树的结构要求元素必须可以比较大小。两种方式定制排序和自然排序。
自然排序:实体类实现Comparable接口
public class Student implements Comparable{
// 方法返回负数代表调用方法的对象小,参数对象大。
// 返回正数代表调用方法的对象打,参数对象小。
// 返回0代表相等
public int comparTo(Object o){
}
}
定制排序:创建TreeSet对象的时候给一个比较器对象。传入的元素可以不需要比较大小,由比较器比较。同时比较器优先级更高。
Set set = new TreeSet(new Comparator(){
// 如果方法返回负数代表o1小,o2大
// 方法返回正数代表o1大,o2小
// 返回0代表相等
public int compare(Object o1,Object o2){
}
});
LinkedHashSet:底层使用数组存储数据,同时维护一个链表。存储数据的去重方式和HashSet一直。同时由于链表的存在,插入顺序就是遍历顺序。效率偏低。
33、Map和Map的实现类
Map接口的特点:Map保存的元素由键值对组成,存放元素时需要同时指定键值。键集是一个不允许重复的集合(Set),值集是一个允许重复的集合(Collection)。
重要实现类:
HashMap:底层使用哈希表存储数据,这个哈希表在jdk1.8之后是数组+链表+红黑树的组成方法。线程不安全,允许null做键也允许null做值。
TreeMap:底层使用红黑树存储数据,要求键值对中的键必须具备比较大小的能力。键比较大小的实现方式有两种,自然排序和定制排序。
HashTable:HashTable的底层实现基本和HashMap一模一样。除了HashTable是线程安全的,另外,HashTable不允许null做键和值。
LinkedHashMap:键的插入顺序就是遍历顺序,其他和HashMap一样。
34、泛型
概念:在声明类,声明方法,声明接口的时候,可以确定一个有一定范围,再创建对象的时候再确定具体类型的一种结构。
应用场合:
1、在类声明的时候使用泛型
public class Animal<T>{// 在类声明的时候指定泛型T,那么这个T在本类中可以作为参数,属性,返回类型的类型,在创建对象的时候在确定。
private T value;// 属性是泛型
public T getValue(){// 方法的返回类型是泛型。
return value;
}
public void setValue(T value){// 参数是泛型
this.value = value;
}
}
class TestAnimal{
public static void main(String[] args){
Animal<String> animal = new Animal<>();// 类的泛型在创建该类对象的时候指定具体类型。
}
}
2、在继承的时候使用泛型
public class Animal<T>{// 在类声明的时候指定泛型T,那么这个T在本类中可以作为参数,属性,返回类型的类型,在创建对象的时候在确定。
private T value;// 属性是泛型
public T getValue(){// 方法的返回类型是泛型。
return value;
}
public void setValue(T value){// 参数是泛型
this.value = value;
}
}
class Dog extends Animal<String>{// 子类在继承父类时就确定类型,这种方式泛型固定,子类不能修改
}
class Cat<T> extends Animal<T>{// 子类继承父类时不确定泛型类型,子类同时采用泛型声明。泛型的具体类型在创建子类对象的时候决定。
}
3、泛型在接口中的时候
public interface Person<T>{
// 泛型不能修饰static成员,接口中的属性都是static的,所以泛型不能在接口的属性上使用
public T methodOne();// 返回类型是泛型
public void method(T t);// 参数类型是泛型
public T methodThree(T t);// 同时是泛型
}
class Teacher implemsnts Person<String>{// 当实现类实现接口的时候决定泛型的具体类型。实现类不能修改泛型的具体类型。
}
class Student<T> implements Person<T>{// 实现类同时泛型,那么就可以由实现类创建对象的时候动态的决定泛型的类型。
}
35、IO流
1、File类:本类代指硬盘上的文件和文件夹。
2、File类的常用方法:
length()文件的大小,单位字节
isFile()如果是文件对象则返回ture,否则返回false
isDirectory()如果是文件夹则返回true,否则返回false
getName()返回文件名(没有路径)
getPath()返回带有路径的文件名
getAbsolutePath()返回带有路径的文件名
createNewFile()创建一个硬盘文件
mkdir()创建一个文件夹
3、File类创建对象
File file = new File("路径");// 路径就是file对象代指的硬盘文件或者文件夹的硬盘路径,这个路径可以是绝对路径,也可以是相对路径。
file.createNewFile();// 方法可以通过file对象在硬盘上创建文件。
4、IO流:
分类:1、按方向分:输入流输出流。2、按内容分:字节流,字符流。3、按功能分:节点流,处理流。
常用流:
1、FileInputStream和FileOutputStream:可以和一个代指硬盘文件的file对象产生流。属于字节流。复制硬盘文件。
2、BufferedReader和BufferedWriter:带缓冲区的字符输入流和字符输出流。带有缓冲区会使得读写效率更高。这两个流属于处理流。需要节点流配合产生流。节点流FileReader和FileWriter().
3、InputStreamReader和OutputStreamWriter:这两个流是字符流。按照语法规定任何一个字符流必须使用Reader或者Writer的子类来创建流。那么这两个中转流可以使用字节节点流来创建字符处理流。中转流的功能就是用字节节点流构建字符处理流。
4、ObjectInputStream和ObjectOutputStream:对象流。属于字节处理流。可以向硬盘文件向输出和读取对象。
需要被对象流操作的对象它的类必须实现Serializable接口。这个接口中没有任何的属性和方法。
36、网络编程
网络编程的基础概念:
1、协议:在网络通讯中,通讯双方指定的统一的通讯标准叫做协议。网络通讯分七层,每层都有自己的协议。TCP协议是网络通讯层协议。
2、IP地址:是在公网通讯中,唯一确定网络终端一种手段。现行的IP地址叫做点分十进制:192.168.24.32,这个IP地址分网络IP和局域网IP。
TCP协议下的网络编程:
使用Socket类来实现TCP下的编程。Socket可以代替我们做TCP协议下的网路通讯,我们只需要创建Socket对象并和Soekct对象建立输入输出流就可以实现TCP协议下的网络通讯。
代码重点:
1、socket通讯需要客户端与服务器端。2、客户端与服务器端通讯需要确定传递信息类型。
客户端代码:
Socket socket = new Socket("IP",端口号);// 客户端需要创建socket对象,因为客户端需要发起请求。
OutputStream os = socket.getOutputStrem();// 获取输出流
socket.shutdownOutput();// 关闭一个方向的流,开启另一个方向的流
InputStream is = socket.getInputStream();// 获取输入流
服务器端:
ServerSocket ss = new ServerSocket();// 创建服务器端的ServerSocket对象
Socket socket = ss.accept();// 服务器监听客户端请求,如果没有请求则服务器端一直处于等待状态,如果有请求那么将获取到客户端请求的socket对象。
InputStream is = socket.getInputStream();// 获取输入流
socket.shutdownInput();// 关闭一个方向的流,开启另一个方向的流
OutputStream os = socket.getOutputStrem();// 获取输出流
37、反射
1、获取Class类对象的四种方式
Class c = Student.class;// 一个类获取自己的类类对象
Student stu = new Student();
Class c = stu.getClass();// 一个对象获取自己的类的类类对象
Class c = Class.forName("com.day21.reflectdemo.Student");// 利用完整类名字符串获取该类的类类对象
Class c = Student.class.getClassLoader().loadClass("com.day21.reflectdemo.Student");// 使用类加载器获取类类对象
2、Class类的常用方法
c.getName();// 获取该对象表示的类的完整类名。
c.getSimpleName();// 过去该对象表示的类的简单类名。
c.getPackage();// 获取报名。
c.getSuperclass();// 获取该类父类的类类对象。
c.getField();// 获取所有的public的属性,包括父类及父类的父类的属性。
c.getDeclaredFields();// 获取一个类的所有属性,包括所有的访问修饰符,不包括父类的属性。
c.getMethods();// 获取所有的public的方法,包括父类及父类的父类的方法。
c.getDeclaredMethods();// 获取该类的所有的方法,包括所有的访问修饰符,不包括父类的方法。
c.getConstructors();// 获取该类所有public的构造方法。
c.getDeclaredConstructors();// 获取该类所有访问修饰符的构造方法。
3、使用反射的方式调用属性
Class c = Class.forName("com.day22.fanshe.Student");// 获取Student类的类类对象。
Object o = c.newInstance();// 通过反射方式获取c对象代表的类的对象。
Field field = c.getField("stuName");// 通过属性名获取单个属性对象。
field.set(o,"Tom");// 第一个参数代表这个属性属于哪个对象,第二参数就是这个对象的这个属性的值。
field.setAccessible(true);// 暴力破解,可以访问私有的属性或者方法。
System.out.println(field.get(o));// 获取o对象的stuName属性的值。
4、使用反射的方式调用方法
Class c = Class.forName("com.day22.fanshe.Student");// 获取Student类的类类对象
Object o = c.newInstance();
// 获取方法,第一个参数是方法名,在方法的后面需要填写方法的参数信息,使用的方式是参数的反射对象,
// 有多个参数则用逗号隔开给出多个参数的反射对象。
Method method = c.getDeclaredMethod("homework",String.class);
method.setAccessible(true);// 暴力破解
// 调用方法的时候第一个参数是对象o,由o这个对象调用方法。从第二个参数开始传参。
method.invoke(o,"chilema");// 运行homework方法。
38、Lambda表达式和StreamAPI
1、使用Lambda表达式实现接口
T1 t2 = () -> System.out.println("Hello World!");
// 实现一个无参的无返回值得方法,那么方法中只有一行代码可以省略大括号,也可以加上。有多行代码必须有大括号
T1 t2 = () -> {System.out.println("Hello World!");};
t2.methodOne();
// 如果函数式接口中的方法有参数,那么再使用lambda表达式的时候需要给出参数类型,以便调用方法时传参
T2 t2 = (String s) -> System.out.println("参数字符串的内容是"+s);
T2 t2 = (s) -> System.out.println("参数字符串的内容是"+s);// 参数的类型实际上也是可以推断出来的,那么就可以省略
T2 t2 = s -> System.out.println("参数字符串的内容是"+s);// 参数只有一个的时候是可以省略小括号的。
t2.methodTwo("小明是个饭盒");
// 如果方法有返回值,那么使用return关键字返回返回值的时候即便只有一行代码也要写大括号
T3 t3 = i -> {return "参数的当前值是:"+i;};
T3 t3 = i -> "参数的当前值是:"+i;
// 如果方法的实现需要多行代码,那么必须有大括号,有大括号必须显示的使用return关键字来返回值。
T3 t3 = i -> {System.out.println("One World,One Dream!");return "参数的当前值是:"+i;};
String s = t3.methodThree(200);
System.out.println(s);
2、四大函数式接口
Consumer<String> t2 = s -> System.out.println(s);// 消费型接口,有一个参数,无返回。
Supplier<String> s2 = () -> {return "金晨";};// 供给型接口,无参,返回值。
Predicate<Integer> p2 = i -> i < 60;// 判断型接口,有一个参数,返回boolean变量。
Function<String,Integer> fun2 = str -> new Integer(str);// 功能型接口,有一个泛型参数,返回一个泛型返回值。
3、Stream流的获取和简单实用
// Stream流的使用分为三步,1:获取Stream流,2:流的中间操作,3:流的终结操作。其中流的中间操作为可选动作,可以有,也可以没有。流的中间操作必须等到流的终结操作时才会执行。所以流的终结操作是必须执行的。
List<Student> list = StudentData.getList();
Stream stream = list.stream();// 从集合对象获取stream流
stream.forEach(s -> System.out.println(s));// 流的终结操作,遍历输出流中的元素
4、Stream流的常用中间操作:过滤
Stream<Student> stream = list.stream();// 获取Stream流
Stream<Student> stream1 = stream.filter(s -> s.getScore() >= 60);// 中间操作:过滤。该方法需要一个判断型函数式接口,过滤掉判断型函数式接口返回为false的记录,保留判断型函数式接口返回为true的记录。
stream1.forEach(s -> System.out.println(s));// 流的终结操作
5、Stream流的常用中间操作:筛选某一区间的记录
list.stream().limit(5).forEach(s -> System.out.println(s));// 截止到参数位置的元素。
list.stream().skip(5).forEach(s -> System.out.println(s));// 跳过参数位置,获取后面的元素。
list.stream().skip(10).limit(5).forEach(s -> System.out.println(s));// 显示第11条到第15条记录。
6、Stream流的常用中间操作:去除重复记录
Stream<Student> stream = list.stream();// 获取Stream流
list.stream().distinct().forEach(s -> System.out.println(s));// 去掉重复记录
7、Stream流的常用终结操作:将Stream流中的数据收集到一个集合中
List<Student> list1 = list.stream().filter(s -> s.getGrade() == 1).
filter(s -> s.getScore() < 60).collect(Collectors.toList());
8、Stream流的常用终结操作:统计Stream流中的数据条目数
long l = list.stream().filter(s -> s.getGrade() == 3).
filter(s -> s.getScore() >= 60).count();