Java
概述
java特点:跨平台性,一次编译,到处运行
Java的加载与执行:
编程语言:
- 机器语言:使用机器指令编程
- 汇编语言:通过大量英文单词来代表计算机指令
- 高级语言:C、Python、Java、C++、php、C#、JS
编译:把其他语言编程的程序转换为机器指令
虚拟机:虚拟的操作系统,模仿真实的计算机
语言版本
- 1990年 Sun公司开始研发,创始人:詹姆斯-高斯林
- 2009年 Oracle收购
- 2014年 Java8
- 2022年10月 Java19
版本
- JavaSE:java标准版,编写控制台,Windows程序
- JavaEE:java企业版,web应用(淘宝、京东)
- JavaME:java微缩版,用于嵌入式设备
环境搭建:
- JDK:Java开发工具包
- bin:存放了很多命令
- java.exe:运行Java程序
- javac.exe:编译源程序,生成字节码文件
- javadoc.exe:生成文档
- bin:存放了很多命令
- JRE:Java运行时的环境
- 环境变量的配置
- JAVA_HOME:指出jdk的安装目录
- path:查找命令而设立的,%JAVA_HOME%\bin
- 集成开发环境
- Eclipse
- IntelliJ Idea
- JDK:Java开发工具包
注释
作用:
- 对已有代码添加注释,是对代码的解释说明,便于阅读和理解
- 暂时不想执行的代码先注释掉,需要的时候再打开注释
- 生成文档
注释的种类
单行注释:以两个正斜线
//
开头,只是对当前行内容注释掉,对下一行不起作用,快捷键Ctrl + /
多行注释:注释多行代码,以
/*
开头,以*/
结尾,多行注释不能嵌套,快捷键Ctrl+Shift+/
文档注释:
/**
文档注释内容*/
,使用java文档工具(JavaDoc
),可以提取出这些注释内容,生成帮助文件或显示在代码提示中在Idea中,工具 => 生成JavaDoc
默认使用gbk编码,如果想生成UTF-8编码的文档,在生成界面中修改:命令行实参:
-encoding utf-8
变量和运算符
变量
变量:代表着内存中的一段空间,用来存储可能变化的数据
变量的声明
变量类型 变量名 [= 变量的初始值];
int n; n = 5000; int n = 5000;//声明的同时进行初始化
注意:
- 局部变量(方法中定义的变量),没有默认值
- 变量必须先声明再使用
变量的作用:
- 输出变量的值
- 使用变量参与运算
变量的作用域(在哪个范围内可以使用)
- 从定义处开始到当前定义的代码块结束的大括号为止
- 在一个作用域中不能有重名的变量名
标识符:
- 定义:Java中的类、包、方法、变量等的名称称为标识符。
- 命名规则:
- 必须以字母、下划线(
_
)或美元符号($
)开头 - 其余的字符可以是字母、下划线、美元符号或数字(长度不限)
- 标识符中不能有空格
- 不能使用Java的关键字和保留字
- 关键字:事先定义好的对编译有特殊意义的单词
- class,int
- 保留字:Java语法中暂时没有使用到该单词,但是保留起来,以后可能会使用
- goto
- 关键字:事先定义好的对编译有特殊意义的单词
- 必须以字母、下划线(
数据类型
Java中数据类型分为两大类
- 基本数据类型:int等
- 引用类型:class
基本数据类型:
整数类型:
byte
:字节,1byte = 8bit、从-128到127short
:短整数,2个字节,16bit,-32768到32767short s1 = Short.MAX_VALUE; System.out.println(s1); short s1 = Short.MIN_VALUE; System.out.println(s2);
int
:整数型,4个字节,32bit,正负21亿多long
:长整数,8个字节,64bit,共19十进制位,后缀:L
进制:一种表示数字的方法,也称为基数或基数系统。常用:二进制、八进制、十进制、十六进制
- 二进制以二为基数,使用0和1来表示数值,10表示2,以
0b
为前缀 - 八进制以八为基数,使用0到7来表示数值,10表示8,以
0
为前缀 - 十六进制以十六为基数,使用0到9,A到F来表示数值,10表示16,以
0x
或0X
为前缀
- 二进制以二为基数,使用0和1来表示数值,10表示2,以
进制转换
其他进制字符串转换10进制整数
Integer.parseInt(10,进制数)
例如: Integer.parseInt(10,2) //把二进制10转换为十进制整数
把十进制的整数转换为其他进制的字符串
String binaryString = Integer.toBinaryString(10);//转2 System.out.println(binaryString); String octalString =Integer.toOctalString(10);//转8 System.out.println(octalString); String hexString =Integer.toHexString(10);//转16 System.out.println(hexString);
浮点数:近似小数,可以表示很大范围的数值,但精度(有效位数)有限
float
:单精度浮点数,4字节存储,32bit,有效位数:6~7位,后缀L
double
:双精度浮点数,8字节,64bit,有效位数约为15位,后缀D
float f1 = 3.14f或3.14f double f1 = 3.14
字符类
char
:2个字节,16bit无符号整数,单引号括''
起来的一个字符使用Unicode编码,能够保存世界上主要语言的主要字符
Unicode常用分类:utf-8,utf-16,utf-32,Java中用的是utf-16
在java中,可以使用\u后面跟着4个十六进制的数字的unicode来表示一个字符
char c2 = '\u4e2d';//中
特殊字符
\t
:制表符\n
:换行符\r
:回车
布尔类型:
boolean
:适合表示比较和逻辑运算的结果,值:true/false
- 10 > 20
数据类型的转换:
不同数据类型不能直接进行运算,需要先转换为相同类型再运算
有两种转换的方法
自动转换:也称为隐式转换,由取值范围小的向取值范围大的转换
强制类型转换: 由取值方位大的向取值范围小的转换,系统不能自动进行
- (int)3.14
- (数据类型)值
当byte、short、char参与运算的时候,会先把它们的数据类型转换为int,再运算
byte b1 = 10; byte b2 = 20; int result = b1 + b2;
字面量;
- 直接写入程序的量
- float:3.14F
- double:3.14D
- long:1000L
- 直接写入程序的量
运算符
定义:也称为操作符,是一种特殊的符号,用来表示数值的运算、赋值和比较
赋值运算符
=
:把=右边的值
赋值给=左边的变量或常量
- 复合运算符
+=
:把变量的值取出来,进行加法运算,然后再把运行的结果放回变量中-=
:把变量的值取出来,进行减法运算,然后再把运行的结果放回变量中/=
:把变量的值取出来,进行除法运算,然后再把运行的结果放回变量中*=
:把变量的值取出来,进行乘法运算,然后再把运行的结果放回变量中%=
:把变量的值取出来,进行取余运算,然后再把运行的结果放回变量中
算术运算符
二元运算符:
+ - * / %
一元运算符:
++、--
++
:自增++变量:前自增
可以理解执行顺序为 int n = 2; int n = n + 1; int k = n//k可以拟为++n的表达式
变量++:后自增
可以理解执行顺序为 int n = 2; int k = n//k可以拟为n++的表达式 int n = n + 1;
--
:自减–变量:前自减
可以理解执行顺序为 int n = 2; int n = n - 1; int k = n//k可以拟为--n的表达式
变量–:后自减
可以理解执行顺序为 int n = 2; int k = n//k可以拟为n--的表达式 int n = n - 1;
表达式:
- 定义:是符合一定语法规则的运算符和运算数的序列
- 表达式的值:表达式中操作数进行运算得到的结果称为表达式的值,任何表达式都有一个值
- 表达式的数据类型:表达式的值的类型就是表达式的数据类型
比较运算符(关系运算符),用来对数值进行比较,表达式的结果为布尔值
==
:比较两个数是否相等!=
:不等于>
:大于<
:小于>=
:大于等于<=
:小于等于
逻辑运算符:对布尔类型进行运算
&&
:与运算符(短路与),两边的布尔类型值同时为true,则整个表达式的值为true,否则为false- 当左边的表达式的结果为false的时候,就不进行右边表达式的计算了,这种效果称为短路
- 如果希望运行后面的表达式,只写一个
&(与)
||
:或运算符(短路或),两边的布尔类型值同时为false,则整个表达式的值为false,否则为true- 当左边的表达式的结果为true的时候,就不进行右边表达式的计算了,这种效果称为短路
- 如果希望运行后面的表达式,只写一个
|(或)
!
:非,一元运算符,对布尔值取相反的结果
三元表达式
- 语法:表达式1?表达式2:表达式3;
- 表达式1的值为布尔类型,如果为true,整个表达式的值为表达式2的值,如果为false,整个表达式的值为表达式3的值
运算符的优先级
按照操作数来划分:一元操作符 > 二元操作符 > 三元操作符
按运算符的类型来划分:算数运算符 > 比较(关系)运算符 > 逻辑运算符 > 赋值运算符
运算符 优先级 类型转换 11//数越大优先级越高 ++、– 10 ! 9 *、/、% 8 +、- 7 >、>=、<、<= 6 ==、!= 5 & 4 | 3 && 2 || 1 =、+=、-=、*=、/=、%= 0
控制台读取数据
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.println(n);
String z = scanner.nextInt();//读到字符串到空格结束
System.out.println(z);
String y = scanner.nextLine();//读到字符串到换行结束
System.out.println(y)
double x = scanner.nextDouble();
System.out.println(x);
流程控制语句
条件控制语句
if语句
语法1:
if(条件){
语句块
}
如果条件为true,就执行大括号中的语句块,如果为false,该if语句
直接结束
语法2:二选一
if(条件){
语句块1
}else{
语句块2
}
如果条件为true,就执行语句块1,如果为false,就执行语句块2
语法3:多选一
if(条件1){
代码块1
}else if(条件2){
代码块2
}else if(条件3){
代码块3
}...else{
代码块n
}
语法4:嵌套
if(条件1){
代码块1
}else{
if(条件2){
代码块2
}else {
代码块
}
}
switch语句
语法:
switch(表达式){
case 值1: 语句块 [break];
case 值2: 语句块 [break];
...
default:
语句块
}
执行过程:
- 计算switch后表达式的值
- 把该值与case后的值进行比较,如果找到相等的,则运行该case后的语句块,而不管后面的case条件是否满足,直到碰到break语句或结束switch语句的
}
为止
注意:
- 表达式的值只能接收
int,byte,short,char,Sring
类型,不支持其他类型 - 不允许有重复的case取值
- 只能进行等值运算
- case之间以及default没有顺序,先判断case,再执行default
- 如果default在写case前面,又没有在default语句中使用break,依然会有向下贯穿的执行效果
循环控制语句
While语句
语句:
while(条件表达式){
循环体(语句块)
}
执行过程:
- 计算条件表达式的值,如果该表达式的值为true,执行该循环体中的语句块
- 然后再判断表达式的值是否为true,如果为true,再执行循环体语句,直到值为false为止
break:退出当前循环(用于循环和switch)
do…while
语句:
do{
循环体语句
}while(条件);
执行过程:
- 先执行循环体内的语句块,然后再判断条件是否为true,如果为true,再次执行循环体,如果为false,则退出循环
与while
的区别:do...while
循环至少会执行一次,while
可能一次都不执行
for
语句:
for(初始化表达式;循环条件表达式;循环后表达式){
循环体语句
}
执行过程:
- 先执行初始化表达式,在整个循环过程中,该表达式只执行一次
- 执行循环条件表达式,如果该表达式的值为true,执行循环体语句块,然后执行循环体后表达式
- 再判断循环条件表达式的值是否为true,如果为true,继续执行循环,若为false,退出循环
注意:
- 在初始化表达式中,可以定义多个类型相同的变量,直接用
,
分隔开
continue
作用:跳过本次循环的剩余语句,执行下一次循环(只能用于循环)
嵌套循环
- 定义:在循环体中包含另一个循环,称为嵌套循环
- 循环体内称为内循环,外面的循环称为外循环
- 执行过程:
- 外循环执行一次,内循环执行一遍
数组
定义:数组可以看成是多个相同的数据类型的数据组合,目的是实现对这些数据的统一管理
数组元素:数组中的每一个数据,称为数组的一个元素,数组中的元素可以是任意类型
数组的长度:数组中元素的个数,数组一旦创建,数组的长度就不能再改变(数组名.length)
数组的声明
- 数据类型[] 变量名;
- 数据类型 变量名[];//极不推荐
数组的创建
- new 数据类型[数组的长度];
- 数组创建后,基本数据类型的元素的默认值为
0(boolean:false,char:'\u0000')
,引用类型元素的默认值为null
;
数组的初始化:数组创建的时候,直接指定元素的值
动态初始化:使用new关键字初始化
int[] scores = new int[](45,67,89); int[] scores; scores = new int[](45,67,89);
静态初始化:不使用new关键字进行初始化
int[] scores = {45,67,89};//只能在声明的同时进行静态初始化
数组的访问:
数组使用下标来访问元素,下标(索引号)从0开始
语法:
数组名[下标/索引号]
数组的第一个元素:数组名[0]
数组的最后一个元素:数组名[数组名.length - 1]
注意:数组越界异常:java.lang.ArrayIndexOutOfBoundsException
为数组元素赋值:数组[下标] = 值
常用算法
最值问题
int max = arr[0]; for(int i = 1 ; i < arr.length ; i++){ if(arr[i] > max){ max = arr[i]; } }
冒泡排序/选择排序
Arrays.sort();
查找问题
foreach语句(语法糖):遍历数组的所有元素
for(数组元素的数据类型 变量名 : 数组名){ 循环体语句块 }
Arrays:该类是数组工具类,包含了数组的一些常见操作,比如排序
Arrays.sort(数组名),对数组中的元素进行升序排序
Arrays.toString(数组名),该方法能够以制定的格式返回数组中每一个元素连接成的字符串
Arrays.copyOf(原数组,新数组的长度):返回新创建的数组长度是指定的,并拷贝原数组中的元素到新数组中
int[] arr = {5,2,10,8,4,5}; Arrays.sort(arr); for (int ele :arr){ System.out.println(ele); } for (int i = arr.length-1;i >=0 ; i--){ System.out.println(arr[i]); } int[] arr2 = Arrays.copyOf(arr,10); System.out.println(Arrays.toString(arr2)); System.out.println( Arrays.toString(arr));
方法的定义与调用
定义:也称为函数,方法是完成某个功能的一组语句,通常将常用的功能写成一个方法
好处:
- 代码重用
- 简化了程序的编写和维护
定义语法
返回值类型 方法名(数据类型 变量名,数据类型 变量名,...){ 方法体语句块 return 返回值; }
- 返回值的类型:用来说明该方法的结果的类型
- 意义:把方法处理的结果返回给调用者,由调用者决定如何处理
- 注意:return语句表示方法结束,退出方法执行,其后不能有语句,否则编译报错
- 方法名:调用的时候使用,引用的标识符
- 方法的参数:
- 方法定义的时候的参数:形式参数,简称形参
- 方法调用的时候的参数:实际参数,简称实参
- 返回值的类型:用来说明该方法的结果的类型
方法调用时的内存情况
- 栈:先进后出,后进先出
- 当调用一个方法的时候,会创建一个栈帧,用来处理当前方法,保存方法中的参数,局部变量,操作数栈
- 当方法结束调用,栈帧被覆盖
方法重载
- 定义:就是在同一个类中允许同时存在一个以上的同名的方法
- 判定重载的依据:
- 方法名称相同
- 参数列表必须不同(参数个数不同,参数类型不同)
- 方法的返回值类型可以相同,也可以不同
- 作用或好处:方法名称重用
方法的值传递
- Java中的参数传递是基于值传递,但是这里的值有两种情况:基本数据类型,引用类型的值
- 对于基本数据类型,值传递意味着将实际的值复制到方法中的新变量。所以在方法内对参数的任意修改者仅限于方法内部,不会影响到调用者中的原始变量
- 对于引用类型,值传递意味着将引用(也就是地址)的值复制到方法中的新变量。所以在方法内部,你可以通过复制的引用来修改对象的状态,这个影响到调用者看到的对象。但是不能改变调用者中引用变量本身的值
public void test(){ int a = 1; int[] arr = {1,2}; System.out.println(Arrays.toString(arr)); method1(a,arr); System.out.println(Arrays.toString(arr)); method2(arr); System.out.println(Arrays.toString(arr)); } public int[] method1(int a ,int[] arr){ arr = new int[]{100, 2}; return arr; } public int[] method2(int[] arr){ arr[0] = 100; return arr; }
面向对象
组成
- 面向对象编程 (OOP)
- 面向对象设计 (OOD)
- 面向对象分析 (OOA)
面向对象和面向过程
面向对象
- 类:是一个模板,它描述了一种特定类型的对象应有的状态和行为
- 对象:是一个类的实例。你可以根据一个类创建多个对象,每个对象都有其自己的状态,但共享同样的行为
类定义的语法结构
[类修饰符] class 类名{
类的成员
}
类的成员
- 成员变量(属性):对象的状态、情况的描述(name、age)
- 成员方法:对象的行为(叫,跑,跳等)
- 编码规则
- 类名:每个单词首字母大写
- 属性名方法名:驼峰命名法(第一个单词首字母小写,后续单词首字母大写,例如hitDog())
- 成员变量和局部变量
- 成员变量:定义在类中,方法外,有初始值,引用类型的初始值是null,基本数值类型是0,布尔类型是false
- 作用域:在当前类中都可以访问
- 局部变量:定义在类的方法中,没有初始值,如果没有被赋予值就不能使用
- 作用域:从定义局部变量的位置开始到定义该局部变量的大括号结束
- 成员变量:定义在类中,方法外,有初始值,引用类型的初始值是null,基本数值类型是0,布尔类型是false
创建对象
语法:
类名 对象名 = new 类名();
使用对象的属性和方法
- 使用点操作符访问对象的属性与方法
创建对象的原理(过程)
- 在堆内存中保存对象的属性
- 在栈帧中保存对象的地址
- 对对象的所有操作只能通过引用完成,一旦引用出栈,如果没有任何引用指向该对象,该对象就成为垃圾
匿名对象
- 创建完对象,直接调用这个对象的方法,这样的对象称为匿名对象
- 注意:匿名对象调用一次方法后,就成为了垃圾
垃圾回收机制
- 原因:当没有对象引用指向原先配给某个对象的内存时,该内存成为了垃圾,JVM的一个系统线程会自动释放该内存
- 注意:垃圾只作用于堆内存,与栈内存无关
构造方法
- 定义:也称为构造器,也是一个方法,但是是一个特殊的方法(与类同名,没有返回类型)
- 特点
- 与类同名
- 没有返回值,不写返回值类型,也不写void
- 也不能在方法中用return语句返回值
- 注意:
- 一个类可以不写构造方法,编译器会自动给这个类添加一个public的构造方法,该构造方法没有参数,称为”默认的空构造器”,如果给类添加了任意构造方法,系统(编译器)就不会再提供默认的构造器了
- 构造方法可以重载,构造方法可以像普通方法一样进行重载,通常一个类中可以有不止一个的构造方法,让使用者根据情况灵活调用
对象创建的详细过程(Dog dog = new Dog();)
类加载,当JVM首次遇到一个类的时候,它会加载这个类。
类初始化:JVM会执行静态初始化块和静态变量的初始化
对象的创建: 使用new关键字创建一个新对象时,JVM会为该对象分配内存,会为成员变量赋予默认值
成员变量初始化
class Dog{ int age = 5;//成员变量初始化 { age = 10; System.out.println("haha") } }
执行代码块(在执行初始化后执行)
class Dog{ int age = 5;//成员变量初始化 { age = 10; System.out.println("haha") } public Dog(int a){ } }
this关键字
定义:代表对象自身的引用
- 一个引用(值是对象的地址)
- 指向调用该方法的当前对象
可以在调用其它构造方法的时候使用
语法:this(其它构造方法的参数列表)
class Tiger{ String name; Tiger(String name){ this.name = name; } Tiger(){ this("泰格"); } Tiger retSelf(){ } }
注意:只能是构造方法的第一条语句
必须使用this的地方
- 区分实例变量和形参
- 调用其他的构造函数
- 返回对象自身的引用
继承
定义:也称为泛化,继承是子类自动共享父类属性的方法和机制,在定义类和实现一个类的时候,可以在一个已经存在的类的基础上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入自己的若干新内容
语法
class 类名 extends 父类名{ }
被继承的类称为父类或超类,继承的类被称为子类或派生类
被继承的好处
- 代码重用
- 使得编程更高效
- 易于维护
注意:子类继承父类除了构造方法之外的所有属性和方法
Java只支持单继承,即只能有一个直接的父类
子类实例化的过程
- 子类实例化的时候,先实例化父类,在实例化子类
- 调用父类的构造方法,再调用子类的构造方法
super关键字
- super():调用父类的构造方法
- 注意:只能出现在子类的构造方法中,其必须是第一行,可以根据super方法的参数决定调用父类的哪个构造方法
- 如果子类构造器中没有使用super(),调用父类的构造方法,那么编译器会默认添加super()。调用父类的无参数构造方法,如果父类没有无参数构造方法,则会报错
- super:表示指向父类的引用
- 在子类的方法中,明确表示使用父类中定义的成员变量或方法
- 在调用super()之前,不能使用super关键字、
- super和this
- super:表示指向父类对象的引用
- this:表示指向本地对象的引用
- super():调用父类的构造方法
- this():调用自己其他的构造方法
包
用途:
- 有助于避免命名冲突
- 包允许将类组合成较小的单元
- 包允许在更大的范围内保护类,数据和方法
语法
package 包名; //例 package com.neu.opp2; com neu oop2 Dog.java
注意:
- 在java中位于包中的类,在文件系统中存放位置必须有包层次相对应的目录结构
- package语句作为java源程序的第一条语句
- 每个源文件只能声明一个包
- 如果没有package语句,则默认为无名包(default package)
- 类的完全限定名 = 包名+类名
常见包:java类库中常用的包
- java.util:工具包(Scanner,Arrays)
- java.lang:默认包,任何程序中,该包自动导入
访问包中的类
访问同名包中的类直接访问,不需要导入该包
访问不同包中的类使用import语句导入其他包中的类
使用import语句注意:
必须在package声明后,类声明前
import可以出现多次
import语句中可以使用通配符(*)
使用通配符会影响编译的速度,比直接导入包中的类要慢,不会影响运行速度
访问权限修饰符
public:共有的,在任何类中都可以访问,没有任何的限制
default(friendly):默认的,包内可见,在该类所在的包中的其他类中都可以访问,包外的类不能访问
private:私有的,不对外公开
private的修饰成员变量和成员方法只能在当前类中使用,其他任何类都不可以访问
protected:受保护的,子类可见和包内可见
可见性:public,protected,default,private
封装
- 面向对象三大特性:封装继承多态
- 封装性:主要理念是”信息隐藏”。封装是将对象的状态(属性、数据)和行为(代码)包装在一个单一的单位(类) 中。这种机制可以防止数据的直接访问。只能通过预先定义的方法(getter,setter)来访问,这样我们就可以隐藏类的内部实现细节,提高了代码的安全性
- 好处:
- 增强安全性
- 提高了代码的复用性和可维护性
- 通过控制成员的访问方式,对成员的访问进行更精细的控制
- 在 java中,封装性是使用访问权限修饰符来实现的
方法覆盖(方法重写)
- 定义:子类继承父类后,子类对继承来的方法进行改造
- 判定规则
- 名称相同
- 参数列表相同
- 返回值类型相同
- 子类覆盖方法的访问权限不小于父类被覆盖方法的访问权限
- 不能抛出父类里没有抛出的异常
引用类型转换
向上转型:也称为父类的引用指向子类对象,把子类自动地转换为父类
- 注意:损失了子类特有的属性和行为
向下转型:把父类转换为子类,需要强制转换
注意:把同一类型向上转换,再向下转换,编译没有错误,否则直接转换就会报错
Animal a = new Cat("小白",100); Dog dog = (Dog)a; //编译不报错 Cat cat = new Cat("小白",100); Dog dog2 = (Dog)cat;//编译报错,因为没有向上转换
instanceof(比较运算符)
定义:用来测试一个对象是否为某个类型的实例
Animal a = new Cat("小白",100);if(a instanceof Dog){ Dog dog = (Dog)a;Jelsef system.out.print]n("a不是Dog类型,不能转换");}
用途:可以在进行向下转型前判断,是否能进行强制转换
多态
- 定义:多种形态,指的是同一个类的不同对象,对同一个行为的不同反应
- 判定多态的条件
- 继承
- 重写
- 父类的引用指向子类对象
- 面向对象的设计原则
- 开闭原则:对修改关闭,对拓展开放
- 多态性也称为动态绑定、迟绑定、运行期绑定
- 在编译时,在Animal中查找hitted()方法
- 如果hitted()方法不是private、static、final,也不是构造方法的话,编译器生成动态绑定指令,否则生成静态绑定指令
- 在运行时,JVM发现是动态绑定指令,根据animal对象的实际类型Dog,先调用Dog的hitted(),如果没有重写hitted(),则调Animal中的hitted()
- 好处:实现了代码的可拓展性
- 练习题:实现停车场收费功能,卡车每小时10元,轿车:每小时5元,公共汽车:每小时8元
static
可以修饰的元素
成员变量(属性)
成员方法
代码块
注意:局部变量不能用static修饰
修饰成员变量(静态变量)
- 类变量,其值所有对象共享
- 生命周期:在类加载后的类初始化阶段,声明静态变量,只要类对象存在,静态变量就存在
- 访问方式
- 实例化后访问 (通过对象来访问):对象名.静态属性名(不推荐)
- 直接通过类名来访问:类名.静态属性名
修饰方法(静态方法)
- 类方法,不需要实例化就可以直接访问的方法
- 访问方式
- 直接访问:类名.方法名()
- 对象访问:对象名.方法名()
- 注意:
- 静态方法只能直接访问静态的成员,不能直接访问非静态的成员
修饰代码块
- 定义:一个类中由static修饰的,不包括在任何方法中的代码块
- 生命周期:在类加载后执行静态代码块,且只能执行一次
- 非静态代码块,在每次创建实例的时候都会进行一次
- 作用:可以用来为静态变量赋予初始值,或者执行一些只执行一次的代码
单例设计模式
定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点
实现过程
构造方法私有化
就只能通过类来访问其中的静态方法
静态方法只能访问静态成员
public class Teacher { private Teacher(){ } private static Teacher teacher = new Teacher(); public static Teacher getTeacher(){ return teacher; } } public class TestTeacher { public static void main(String[] args) { Teacher.getTeacher(); } }
abstract
修饰类:抽象类,不能被实例化的类
abstract class 类名{ }
- 作用:让子类去继承
修饰方法:抽象方法,只有方法的声明,没有方法的实现(没有实现体)的方法
abstract class 类名{ abstract 方法声明; }
- 注意:
- 抽象方法必须放在抽象类中
- 作用:让子类去重写
- 注意:
在下列情况下,类必须声明为abstract:
- 当一个类中包含抽象方法时
- 当一个类继承了父类的抽象方法,又没有全部重写的时候
final
可修饰的元素
类:不能被继承
方法:不能被重写
成员变量和局部变量:不能被改变
public class Student{ final int score;//成员变量一旦用final修饰就没有默认值了 //通过构造方法为final变量赋予初始值 public Student(int score){ this.score = score; } }
注意:
- 修饰成员的时候,声明时赋值,或者在构造方法中赋值,否则报错
常量
- 定义:一旦赋值就不能再改变
- 命名规范:标识符全部字母大写,单词间用下划线分割
- 注意:如果常量是类的成员,建议添加static修饰,成为类变量
接口
定义:接口对类来讲是一套规范,是一套行为协议
语法规则
interface 接口名{ [public static final]常量 = 初始值; [public abstract]抽象方法 }
类实现接口
class 类名 implements 接口名{ }
实现一个接口意味着:去实现(重写)这个接口中所有抽象的方法或者实现部分方法,再把自己声明为抽象的
一个类可以同时实现多个接口
class 类名 implements 接口名1, 接口名2,...{ }
一个类可以继承一个父类,实现多个接口
class 类名 extends 父类名 implements 接口名1, 接口名2,...{ }
接口继承接口
interface 接口名 extends 接口名1,接口名2{ }
接口的好处:在不同类型中抽取了共同的行为,他们在实现接口的功能上是可替换的
面向对象的设计原则:针对接口编程,而不针对实现编程
接口作为方法返回值的意义:
实际就是把这个接口的实现类对象返回给调用者
接口的补充
从jdk8开始,接口添加了一种新功能——默认方法,默认方法允许接口方法定义实现,而所有子类都拥有该方法的实现
好处:提供一种扩展接口的方法啊,而不破坏现有的代码
注意:默认的方法在子类中可以被重写
interface 接口名{ [public static final]常量 = 初始值; [public abstract]抽象方法 默认方法(default 返回值类型 方法名(参数列表){ 方法体 }) }
异常处理
概述
- 定义:程序运行时错误
- Java中错误的种类
- 编译错误:语法错误导致
- 运行时错误(异常)
- 异常的危害:
- 程序被终止
- 内存泄漏
异常类的继承关系
- Throwable:可以被抛出的错误
- Error:指的是应用程序运行期间发生的严重错误,如:虚拟机内存耗尽,堆栈溢出,一般是灾难性的,没有必要使用异常处理机制
- Exception:可以通过异常处理机制处理的错误
- 以IOException代表的异常:检查性异常,依赖于程序的运行环境,无论如何也不可能完全通过测试排除掉
- 以RuntimeException为基类的异常:非检查性异常,通过反复测试能够把所有异常排除掉
异常处理机制
异常如何产生
在程序运行过程中,如果发生异常事件,系统就会发出错误报告,这时候系统将会产生一个异常对象,该对象封装了异常相关的信息,并将其提交给Java运行时环境
异常如何处理
- 自行处理:把可能发生的异常语句封入到try块中,而处理异常的语句封入到catch中
- 回避处理:在方法声明中包含throws语句,通过潜在的调用者来处理异常
异常处理语句
- try…catch
- try:该代码块中放入可能发生异常的语句
- catch:用来捕获并处理try块中抛出的异常代码块,没有try块,catch不能单独存在,可以有多个catch块用来捕获不同的异常
- 每次从try块中抛出异常,系统会从上向下往每个catch块中传递参数,直到遇到一个类型匹配的catch块为止
- finally语句
- 放在try…catch语句后
- finally语句中的代码块不管异常是否发生,是否被捕获到,总是要执行
- 即使try块和catch块中使用return或break语句,finally语句块都是要执行
- 当执行System.exit():表示退出系统,finally语句不会再执行
- finally语句主要用来释放程序占用的系统资源
- throws语句
- 如果一个方法中的语句块在运行时可能发生异常,但是不确定如何处理,则可以在程序方法的声明后使用throws关键字表明方法中可能有这些异常抛出
- throws语句后面可以跟多个异常类型,中间用逗号分隔
- throw语句
- 异常是使用throw语句抛出的,可以使用throw语句引发明确的异常
- throw语句用在方法体中,表示抛出异常,由方法体语句处理或采取回避处理
- 与throws的区别
- throws表明方法可能抛出异常,写在方法的声明中,对调用者的提示
- throw表明抛出异常对象,写在方法体内
- 对检查性异常和非检查性异常的处理
- 非检查性异常:可以处理,也可以不处理(需要大量的测试找出所有异常)
- 检查性异常:必须处理(要么处理,要么回避)
- 为什么不都是异常处理机制
- 大量使用异常处理语句会使得代码的可读性差
- 对代码进行异常检查会使得编译较慢
- 异常对象的常见方法
- getMessage():得到异常的消息
- printStackTrace():打印栈(堆栈)的跟踪信息
- 方法调用堆栈
- 定义:方法A调用方法B时,只有完成方法B,A才能完成,先执行的方法总是后完成,后执行的方法总是先完成
- 在方法调用的堆栈中的某个方法中的语句抛出异常,会导致所有方法都不能正常结束,如果不想出现这种情况,就要使用java的异常处理机制进行处理
- 自定义异常:
- 定义:如果Java提供的异常类型不能满足程序设计的需要,可以自定义异常类型
- 用户自定义的异常应该继承Exception或其子类
- 检查性异常:Exception
- 非检查性异常:RuntimeException
- 注意
- 自定义异常的名称很重要,要尽可能反映异常的功能
- 可以添加一个有参数的构造方法来传递异常的信息
- 应该自己检查异常发生的条件,抛出异常
工具类
API
- 定义:应用程序编程接口
- 是JDK提供的各种功能的java类和接口等
Object
Object是所有类的超类,Object是java中唯一一个没有父类的类
一个类可以不是Object的直接子类,但一定是Object类的子类
意义:在Object类中定义的方法,在任何类中都可以使用
equals()方法
public boolean equals(Object obj)
功能:比较两个对象的引用是否相等
hashCode()方法
public int hashCode()
- 功能:返回一个对象的哈希码,一个对象的哈希码是代表对象的一个整数,比作对象的身份证号,默认的哈希码是一个对象内存地址做某种转换得到的,所有不同的对象有不同的哈希码
- java的规定,如果调用equals方法返回两个对象相等的,那么在这两个对象上调用hashCode()方法返回值的整数也必须是相等的
toString
public String toString()
功能:返回一个字符串,格式:完全限定名@哈希码(十六进制整数)
equals()方法和==的区别
equals()方法只能比较引用类型,不能比较基本数据类型,==,既可以比较引用类型,也可以比较基本数据类型
默认情况下(子类没有重写Object类的equals()方法),equals()方法只能比较对象的引用是否相同
==如果比较引用类型就是比较地址是否相同
包装类
在java中基本数据类型变量不是对象,通过包装类将基本数据类型看作对象
目的:把基本数据类型转换为对象,就可以同其他的对象统一处理使用了
每一种基本数据类型都有对应的包装类,大多数包装类的命名方法为:基本数据类型单词首字母大写,有两个不同
- char=》Character
- int=》Integer
自动装箱和拆箱
自动装箱:把基本数据类型转换为包装类
自动拆箱:把包装类中的值取出来
如:
Integer i = 10; int j = i;
基本数据类型转换为字符串
- String s = 10+””;
- String s = Integer.toString(10);
把字符串转换为基本数据类型
- int n = Integer.parseInt(“100”);
String
定义:一组不可改变的Unicode字符序列,对它的任何修改实际上产生了一个新的字符串
字符串的创建
静态方式:使用字符串字面量赋值
String s = "abc";
- 两个采用静态方式创建的字符串,如果字面量相同,那么在方法区的常量池中,只产生一个字符串对象,即这两个字符串变量的引用相同
动态方式:使用内存分配符(new)
String s = new String("tom");
字符串的常用方法
- boolean equals(Object obj):比较字符串的内容是否相等
- boolean equalslgnoreCase(String str):字符串忽略大小写比较
- String toUpperCase():把字符串中小写字母转换为大写
- String toLowerCase():把字符串中大写字母转换为小写
- char charAt(int index):返回字符串中指定索引处的字符,如果index超出指定的范围,抛出字符串索引越界异常,index从0开始
- int length():返回字符串中字符的个数
- String substring(int beginindex):从指定的索引处开始截取字符串,到字符串结束位置
- String substring(int beginindex,int endindex):从指定的索引处开始截取字符串,到结束索引号为止(不包括结束索引对应的字符)
- int indexOf | lastindexOf(String|char):在字符串中查找一个指定的字符串或字符第一次或最后一次出现的位置,如果没有找到,返回-1
- String trim():返回字符串去掉首尾空格后的字符串
- String split(String str):使用指定的分隔符分割当前字符串,返回分隔后的元素组成的数组
- String replace(char|String,char|String):使用新的字符串或字符替换原来的字符串或字符
- int compareTo(String):比较两个字符串的大小,相等返回量,当前字符串大返回大于零的数,否则返回小于零的数
StringBuffer
- 定义:可以改变的字符序列,可以动态改变,而不产生额外的对象
- 构造方法:
- StringBuffer():创建了一个其中不带有字符串的字符串缓冲区,初始容量为16个字符
- StringBuffer(int capacity):创建一个具有指定容量的字符串缓冲区
- 常用方法
- append(String value):在原来字符串后面追加字符串
- toString():得到StringBuffer中的字符序列
- int length():返回字符的个数
- int capacity():得到缓存区容量的大小
- setChar(int index,char c):修改index处的字符
- insert(int index,String str):在index处插入指定的字符串
- deleteCharAt(int index):删除指定index处的字符
- delete(int start,int end):删除指定索引号之间的字符
StringBuilder
- StringBuilder设计用来作为StringBuffer的简单替代,用在字符串缓冲区被单线程方法使用的情况
- 其他使用方法与StringBuffer完全一样。
Math
Math类提供了大量数学运算的方法
Math类是final类,不能被继承
Math类中的方法都是静态的,不用实例化直接通过类名访问
常量
- Math.PI:圆周率
- Math.E:自然对数的指数
常用方法
abs():求绝对值
ceil(double):向上取整
floor(double):向下取整
random():返回0到1之间的随机数
(int)(Math.random()*35)+1;
round(double):四舍五入
sqrt(double):开方
Date
- 包:java.util.Date
- 表示特定的瞬间,精确到毫秒,在内部,保存自1970年1月1日00:00:00到某一时刻间隔毫秒数
- getTime():得到内部时间毫秒数,long类型
- new Date():得到一个当前时间的日期对象
- new Date(long time):根据参数指定的毫秒数,创建一个对应的日期对象
- 该类的许多方法都过时了,过时的方法在未来高版本的JDK中可能就不在支持了,建议别用
- toString():返回日期的字符串表示形式
- befor(Date):判断当前日期是否在参数给定的日期前,在之前,返回true,否则返回false
- after(Date):判断当前日期是否在参数给定的日期后,在之后,返回true,否则返回false
Calendar
包:java.util.Calendar
是一个抽象类
获取Calendar类的实例
Calendar.getInstance();静态方法,得到当前时间对象
设置时间
- set(int field,int value):根据指定字段设置日期值
- set(int year,int month,int date,int hours,int minutes,int seconds):设置日期,月份从0开始
得到指定的时间部分
- get(int field):field可以使用Calendar类中定义的常量来设置
把Date转换为Calendar
- setTime(Date)
把Calendar转换为Date
- Date.getTime
为给定的日历字段添加或减去指定的时间值
- add(int field,int num)
SimpleDateFormat(重要)
包:java.text
构造方法:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
模式字母
- yyyy:4位年
- MM:月
- dd:日期
- HH:0-23(小时)
- hh:0-11(小时)
- mm:分钟
- ss:秒
String format(Date):把日期格式化为指定格式的字符串
Date parse(String):把字符串转换为日期对象
集合框架
概述
- 集合也称为容器类,用来储存对象
- 数组的问题
- 长度一旦定义就不能再改变
- 数组要求每一个元素在内存中是挨着存放的
- 常用操作:增删改查
- 与数组比较
- 数组定长,集合大小可以动态调整
- 数组中元素可以是基本数据类型和引用类型,集合中只能储存引用数据类型
集合框架
- Collection:接口
- List:接口,是Collection接口的子接口,元素特点:有顺序,可重复
- Set:接口,是Collection接口的子接口,元素特点:无顺序,不可重复
- Map接口:键值对储存
- Iterator:接口,迭代器,对Collection中的元素进行遍历访问
ArrayList
是List接口的一个实现类
数据线性存储(元素在物理存储的时候,是挨着存放的) 特性跟数组相近,简称为动态数组
构造方法
ArrayList():创建一个初始容量为10的空集合 ArrayList(int capacity):创建一个具有初始容器为capacity的空集合
常用方法:
- add(Object):把元素添加到集合最后
- get(int index):根据索引号得到对应的元素,如果索引号超出一定的范围,抛出异常
- add(int index,Object obj):把元素插入到index位置,index不能超出集合中元素的个数
- size():返回集合中元素的个数
- contains(Object obj):判断集合中是否包含参数指定的对象,包含返回true,否则返回false
- 注意:比较的时候,使用对象的equals()方法进行比较
- Object remove(int index):移除参数指定位置的元素,返回移除后的元素
- boolean remove(Object obj):调用equals()方法移除参数指定的单个元素(移除一个元素后,比较终止),返回true表示移除成功
- set(int index,Object obj):在指定索引处,用obj替代原来的元素
- indexOf(Object obj):查找对象第一次出现的位置,返回索引号,没有找到,返回-1
- lastindexOf(Object obj):查找对象最后一次出现的位置,返回索引号,没有找到,返回-1
- toArray():把集合中的元素导入数组,返回数组
LinkedList
- 是List接口的实现类
- 数据结构采用的双向链表,元素物理存储位置不要求连续
- 优缺点
- 优点:插入和删除节点效率高
- 缺点:随机访问效率不高
- 常用方法:
- addFirst(Object):把元素插入到集合第一个位置处
- addLast(Object):把元素插入到集合的最后一个位置处
- getFirst():得到第一个元素
- getLast():得到最后一个元素
- removeFirst():移除第一个元素
- removeLast():移除最后一个元素
- 使用ArrayList还是LinkedList,取决于对选择的数据结构做何种操作比较多
- 插入和删除操作比较多,选择LinkedList
- 随机访问(根据索引得到元素)比较多,选择ArrayList
泛型
定义:类型参数,允许在编译时限制和检查类或方法中的类型参数
好处:提高代码的可读性,健壮性和重用性,同时减少了运行时类型错误的风险
目的:在编译时提供类型的检查,避免在运行时出现类型转换错误
语法:
- 在类或接口后面跟一个尖括号,其中包含类型参数,类型参数可以是类(如T),或者接口(如E)
如何限制泛型的类型
使用界定符来限制泛型类型的范围
public class Box <T extends Animal & Compareable>{ private T content; public T getContent(){ return content; } public void setContent(T content){ this.content = content; } public static void main(String[] args) { Box<Animal> box = new Box<>(); box.setContent(new Animal()); System.out.println(box.getContent()); } }
虽然使用的是extends关键字,但是它同时表示类的继承和接口的实现
HashSet
是set接口的实现类
构造方法
HashSet():构造了一个容量为16的空间
常用方法:
- boolean add(T t):添加元素到集合中,添加成功,返回true
- boolean remove(T t):移出元素,移出成功,返回true
- int size():得到元素的个数
- boolean isEmpty():是否为空合集(size() == 0)
- clear():清空集合
- contains(T t):判断元素在集合中是否存在,如果存在,返回true
Iterator
- 该接口用于遍历集合(Collection),它提供了三个主要方法
- boolean hasNext():检查集合内部是否有更多的元素
- next():返回迭代的下一个元素,并将光标向前移动一步
- remove():从底层集合中删除迭代器最后返回的元素
- 使用时注意:先要通过next()返回元素,,否则运行异常(IllegalStateException)
- 注意:单向操作
ListIterator
- ListIterator接口拓展了Iterator,提供了更强大的列表迭代功能
- 提供了双向的迭代和对列表的修改功能
- hasPrevious():检查是否有上一个元素,有则返回true
- previous():返回迭代的上一个元素,并将光标向后移动一步
- add(T t):会在光标当前位置插入一个元素,并使光标移动到插入到元素之后
- set(T t):操作的是最后一次调用next()或previous()返回的元素,光标的位置不变
Collections
- 提供了一些操作Collection接口子类的方法
- 常用方法
- max(Collection):找出Collection集合中最大的元素
- 要求Collection元素实现Comparable
- max(Collection,Comparable):找出Collection集合中最大的元素,使用第二个参数作为比较器
- Comparable:接口,可以比较大小的,某个类实现该接口,就意味着这个类的对象是可以比较大小的
- int compareTo(Object):返回0表示相等,返回大于0的数,表示当前元素大于参数元素
- Comparator:接口,比较器接口,单独定义一个类,实现该接口
- int compare(Object,Object)返回0表示相等,返回大于0的数,表示当前元素大于参数元素
- min(Collection)
- min(Collection, Comparator)
- sort(List):对List接口集合元素进行排序
- sort(List,Comparator):对List接口集合元素进行排序,使用第二个参数作为比较器
- max(Collection):找出Collection集合中最大的元素
HashMap
- Map接口的实现类,键值对存储,键不可以重复,值可以重复,值可以为null
- 常用方法:
- put(Object,Object):将键和值存储到map集合中,如果key存在,则替换值
- Object get(Object key):根据键得到值
- Object remove(Object key):根据key,移除键值对,返回被移除的值
- boolean containsKey(Object key):判断是否包含指定的键,包含返回true
- boolean containsValue(Object key):判断是否包含指定的值,包含返回true
- int size():得到键值对的个数
- Set keySet():得到键的集合
- Collection values:得到值的集合
- Set entrySet():得到键值对集合
- Map.Entry:键值对类型
- getValue():得到键值对中的值
- getKey():得到键值对中的键
Arraylist扩容 与 hashmap 扩容机制
- ArrayList扩容:当添加元素超过当前容量的时候,通常会将数组容量增加到原来的1.5倍,并将旧的数组的内容拷贝到新数组中
- HashMap扩容:当HashMap中的元素超过负载因子与当前容量的乘积时,HashMap会扩容,通常将容量增加一倍并重新计算每个元素的位置
HashMap与TreeMap的区别
- HashMap使用的是哈希表,提供常数时间的访问速度
- TreeMap使用红黑树,按照键的顺序排序
- HashMap允许一个null键和多个null值
- TreeMap不允许null键
HashMap与HashTable
- HashMap非线程安全,HashTable线程安全
- HashMap允许一个null键和多个null值
- HashTable不允许null键和null值
- HashMap通常比HashTable更快,因为它不需要同步
List,Set,Map的区别
- List是有顺序的元素集合,可以包含重复的元素
- Set是无顺序的元素集合,不包括重复的元素
- Map是由键值对组成的元素,其中键是唯一的
文件与流
文件管理
- java.io.File
- 该类主要针对文件或文件夹的路径名的管理
- 作用
- 文件属性的管理
- 文件的检查
- 文件的删除
- 不包括文件内容的管理
- 常用方法
- boolean exists():是否存在
- boolean isFile():判断是否为文件
- boolean isDirectory():判断是否为文件夹
- boolean createNewFile():创建新文件,创建成功,返回true,文件已经存在,返回false
- boolean mkdir():创建文件夹
- boolean mkdirs():创建文件夹,会创建路径中不存在的文件夹
- String[] list():读取文件夹中的信息
- File[] listFiles():读取文件夹中的信息,返回File型
- String getName():得到文件名
- String getPath():得到文件路径
- long length():得到文件大小,单位为字节
- boolean delete():删除文件或文件夹
- boolean renameTo(File):重命名
流
数据流:一串连续不断的数据的集合
流的分类:
- 流的方向:输入流和输出流(以我们的程序为主来说)
- 处理的的单位:字节流,字符流
- 功能分:节点流和处理流
- 节点流:直接从数据源读取数据
- 处理流:在其他数据流的基础上,对数据进行进一步处理
FileInputStream(对文件读取字节输入流,节点流)
- 构造方法
- FileInputStream(String filePath)
- FileInputStream(File file)
- 常用方法
- int read():从流中读取一个字节数据,返回值为读取到的内容,返回出-1,表示读取到文件结尾了
- int read(byte[]):从流中读取数据到字节数组中,返回实际读取的字节数
- 构造方法
FileOutputStream(对文件写入字节输出流,节点流)
- 构造方法
- FileOutputStream(String filePath)
- FileOutputStream(String filePath,boolean append):append如果为true,表示追加内容
- 常用方法
- void write(int):写入一个字节数据到流中
- void write(byte[]):写入多个字节数据到流中
- 构造方法
FileReader(对文件操作,输入流,字符流)
常用方法
int read():读取一个字符
int read(char[]):读取数据到字符数组中
package com.io; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; public class StreamDemo4 { public static void main(String[] args) throws IOException { FileReader fileReader = new FileReader("D:\\c.txt"); char[] cs = new char[1024]; int read = fileReader.read(cs); String s = new String(cs,0,read);//把字符数组指定范围内的字符转换为字符串 System.out.println(s); fileReader.close(); } }
FileWriter(对文件操作,输出字符流,节点流)
- 构造方法
- FileWriter(String filePath)
- FileWriter(File file)
- FileWriter(String filePath,boolean append):append表示是否追加,如果为true追加
- 常用方法
- void write():写入一个字符
- void write(String):写入一个字符串
- 构造方法
BufferedReader(带缓冲的字符输入流,处理流)
- 构造方法
- BufferedReader(Reader)
- 常用方法
- String readLine():读取一行数据
- 构造方法
PrintWrite(字符流,处理流,输出流)
- 构造方法
- PrintWrite(Writer);
- PrintWrite(String filePath)
- 常用方法
- println(String):写入一行数据到流中,自动加上换行符
- 构造方法
序列化和反序列化
- 序列化:将对象转换为字节序列的过程
- 反序列化:将字节序列转换为对象的过程
- 序列化的过程
- 让序列化的对象实现一个Serializable的接口
- 创建ObjectOutputStream对象,使用ObjectOutputStream的writeObject(Object),把对象写入到流中
- 反序列化的过程
- 创建ObjectInputStream对象,使用ObjectInputStream的readObject(),从流中读取对象
冒泡排序
int[] arr = new int[]{1,2,3,4,6,9,8,7,5};
int temp = 0;
for(int i = 0 ; i < arr.length-1; i++){
for (int j = 0; j < arr.length - i - 1; j++) {
if(arr[j] < arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
二维数组
int[][] arr = new int[][]{{4,3,7},{1,2,5,8}};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j]);
}
}
线程
概述
进程:正在运行的程序
- 多任务:多进程
- 多进程的作用:增加了CPU的利用率
线程:一个进程同时运行多个任务,每个任务称为一个线程
- 定义:是一个程序内部的顺序执行流
进程与线程的区别
- 本质区别:每个进程有自己一套变量,而多线程共享数据
- 与进程相比,线程是轻量级的,创建和销毁一个线程比启动和销毁一个进程开销要小得多
创建线程
继承Thread类,重写run方法
public class MyThread extends Thread{ @Override public void run() { for (int i = 0 ; i < 5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } } MyThread myThread = new MyThread(); //启动线程 myThread.start(); MyThread myThread1 = new MyThread(); myThread1.start();
让类实现一个Runnable接口,把该类的对象作为Thread类的构造参数
public class MyThread2 implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String[] args) { MyThread2 my = new MyThread2(); Thread thread = new Thread(my); thread.start(); Thread yuanshen = new Thread(my); yuanshen.start(); } }
多线程的意义
- 可以在一个程序中实现多个任务
- 提高资源利用率
线程的状态(Thread.State)
- NEW:新生态,线程对象被创建,但是没有启动
- RUNNABLE:可运行状态(就绪状态),已经启动了start()方法,有可能正在运行,也可能等待cpu调度执行
- BLOCKED:阻塞态,由于当前线程缺少资源,无法继续运行
- WAITING:等待状态(等待唤醒)
- TIMED_WAITING:等待定时器结束
- TERMINATED:结束状态
常用方法:
Thread.sleep(long):休眠指定的毫秒数
Thread.currentThread():得到当前线程
getName():得到线程名
getState():得到线程状态
isAlive():判断当前线程是否还在运行
join():加入一个线程,直到它结束
package com; public class Mather extends Thread{ @Override public void run() { System.out.println("妈妈开始做饭..."); System.out.println("妈妈发现酱油没有了"); System.out.println("妈妈等待儿子打酱油"); Son son = new Son(); son.start(); try { son.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("妈妈继续做饭"); } public static void main(String[] args) { Mather mather = new Mather(); Son son = new Son(); mather.start(); // son.start(); } } class Son extends Thread{ @Override public void run() { System.out.println("儿子去打酱油"); for (int i = 1; i <= 10; i++) { try { Thread.sleep(100); System.out.println("去了"+i+"分钟"); } catch (InterruptedException e) { throw new RuntimeException(e); } } System.out.println("酱油买回"); } }
线程同步
原因:有时两个或多个线程可能会试图访问同一个资源,可能导致数据不一致的现象
解决:使用线程同步,确保对受保护资源的访问必须是串行化的
同步化代码块
package com; public class Ticket implements Runnable{ private int no = 100; @Override public void run() { try { Thread.sleep(1); while (no >=1){ synchronized (this){ if(no >= 1){ System.out.println(Thread.currentThread().getName()+","+no); no--; } } } } catch (InterruptedException e) { throw new RuntimeException(e); } } public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(ticket).start(); new Thread(ticket).start(); } }
同步化方法和同步化代码块
- 不同点
- 同步化代码块可以锁定任意对象,同步化方法只能锁定当前对象
- 同步化方法,把当前方法内的所有代码都锁定了,使得方法的执行完全串行化,有可能锁定的范围过大,同步化代码块可以更精确的控制要定的代码
- 同步化方法在方法的声明中可以看见该方法是受到保护的,而代码块不行
- 不同点
线程的优先级
- 线程调度器是根据线程的优先级从就绪状态中选择运行的线程
- 线程优先级是高度依赖操作系统的
- 我们程序的算法不要依赖与线程的优先级
- setPriority(int):值的范围1~10,默认是5
死锁:两个线程彼此等待对方占据的锁
程序调试
- 作用:
- 查看程序执行过程
- 查看内存数据的变化情况
- 查找程序逻辑错误
- 步骤
- 设置断点
- 进入Debug模式
- 运行停止在断点处(当前代码没有执行)
- 步过(执行当前行代码) Idea快捷键:F8
- 恢复程序(执行到下一个断点处,如果没有下一个断点,程序执行到结束) Idea快捷键:F9
- 停止(直接停止程序调试) Idea快捷键:Ctrl+F2
- 使用评估窗口:计算表达式的值