目录
JAVA SE
JAVA入门
什么是java
- Sun公司(Stanford University Network)1995年推出的一种高级编程语言。
- 主要应用在互联网程序的后台开发和数据处理等方面。
- java的特点:易上手、面向对象、跨平台、安全可靠、支持多线程、支持网络编程且方便、编译与解释并存。
面向过程与面向对象
- 面向过程:性能高、但难以扩展、难以维护,一般用于系统底层开发。
- 面向对象:易维护、易扩展、易复用、但是性能不如面向过程高。
Java与C++的区别与相同
- 都是面向对象的高级语言,都支持封装、继承、多态的特性。
- java不提供指针直接访问内存,相对内存更安全。
- java是单继承,C++支持多继承。java的接口可以多继承。
- java由内存自动管理机制,无需程序员控制。
二进制
- 逢二进一
- 二进制十进制互相转换
- 十进制转二进制:除2倒取余法。
- 二进制转十进制:8421码。
- 注意B和b的区别
- 字节和比特。
###常用的DOS命令
命令 | 操作符 |
---|---|
切换盘符 | 盘符名: |
查看当前文件夹 | dir |
进入文件夹 | cd 文件夹名 |
返回上一级 | cd .. |
推出到根目录 | cd \ |
清屏 | cls |
java虚拟机——JVM
-
JVM(Java Virtual Machine ):Java虚拟机,简称JVM,是运行所有Java程序的假想计算机,是Java程序的运行环境之一,也是Java 最具吸引力的特性之一。我们编写的Java代码,都运行在JVM 之上。
-
JVM带来的好处——跨平台
任何软件的运行,都必须要运行在操作系统之上,而我们用Java编写的软件可以运行在任何的操作系统上,这个特性称为Java语言的跨平台特性。该特性是由JVM实现的,我们编写的程序运行在JVM上,而JVM运行在操作系统上。每种操作系统上运行的虚拟机不同,但他们可以执行相同的java程序产生相同的效果。
- 补充:基本类型所占的空间大小是固定的。不会随着硬件变化而变化,这也是java程序更具可移植性的原因之一。
JRE和JDK
- JRE(Java Runtime Environment):是Java程序的运行时环境,包含jvm和运行时需要的核心类库
- JDK(Java Development Kit):Java程序开发工具包,包含JRE和开发人员使用的工具(比如编译器)。
- JDK安装后,要配置环境变量。是为了方便在任何目录下进行编译和运行java程序,环境变量的配置在此不做赘述。
Oracle JDK 与 Open JDK
- OracleJDK与OpenJDK之间没有重大差异。OpenJDK与OracleJDK是非常接近的,OpenJDK完全开源,Oracle JDK是OpenJDK的一个实现,不是完全开源的。
- 对于Java 7,OpenJDK是Sun捐赠的HotSpot源码,OracleJDK增加了一些商业功能的部分,例如部署代码,其中包括Oracle的Java插件和Java WebStart的实现,以及一些封闭的源代码派对组件,如图形光栅化器,一些开源的第三方组件,如Rhino,以及一些零碎的东西,如附加文档或第三方字体。
- 开发使用一般用OracleJKD,而研究JVM源码或者很底层的一些JDK源码,一般使用OpenJDK。
- OracleJDK更加稳定,性能更高。
Java程序开发三步骤
- 编写程序,编写的程序文件后缀名为.java,其中包含main方法作为程序的入口。
- 在终端,进入该文件所在的目录,使用命令
javac 文件名.java
进行编译,编译完成后,在该目录下生成对应的.class
文件。 - 运行程序:使用命令 java 类名(不加后缀)。
注意
- 编译和运行是两个不同的过程和步骤。编译是指将java文件翻译成jvm所认识的class文件,也就是字节码文件,编译阶段,java编译器会首先进行一次检查,如果发现错误就会提示出来,也就是编译错误,会导致编译失败无法生成class文件。编译成功说明语法没什么问题,生成了class文件,但此时也不代表程序一定没有问题。
- 运行就是指拿着class文件交给jvm去执行。程序发生问题时jvm会抛出异常或者错误信息。如果class文件没有问题,程序就可以正常运行啦。
java初级
开发工具IDEA
主题
网上下载即可
字体
- 通过菜单栏上的
File->Settings->Editor->Font
修改字体。 - 设置按住ctrl+鼠标滚轮控制字体大小。
快捷键
功能 | 快捷键 |
---|---|
导入包,自动修正代码 | Alt+Enter |
删除光标所在行 | Ctrl+Y |
复制光标所在行的内容,插入光标位置下面 | Ctrl+D |
格式化代码 | Ctrl+Alt+L |
单行注释 | Ctrl+/ |
选中代码注释,多行注释,再按取消注释 | Ctrl+Shift+/ |
移动当前代码行 | Alt+Shift+上下箭头 |
补全代码 | ctrl+. |
修改快捷键
File->Settings->keymap->Main menu->code->Completion->Basic
快捷生成注释
模板
文件模板
代码快捷键
常用术语
注释
- java中有两种注释:
- 单行注释 //开头,换行结束。
- 多行注释 /* 开头,*/结束。
关键字keywords
- 可以理解为java中具有特殊含义,特定用法的单词。不能改变用法。java中的常用关键字有如下几个:
关键字 | 含义 |
---|---|
class | 类 |
public | 公共权限 |
static | 静态的 |
final | 终态的 |
try | try…catch…finally |
catch | |
finally | |
protected | 受保护权限 |
default | 默认的 |
private | 私有权限 |
int | 整型 |
byte | 字节 |
boolean | 布尔 |
true | 是 |
false | 非 |
if | if…else |
else | |
while | while循环,会与do连用 |
char | 字符型 |
long | 长整型 |
float | 浮点型 |
double | 双精度浮点型 |
import | 导包 |
package | 所属包 |
return | 返回 |
this | 当前类的某方法或构造器的隐藏参数 |
super | 父类的引用 |
void | 空 |
extends | 继承 |
interface | 接口 |
implements | 实现 |
instanceof | 测试对象是否为类的实例 |
new | 创建对象或数组 |
null | 引用未指向对象 |
break | 跳出循环 |
continue | 进入下一次循环 |
switch | switch…case |
case | |
for | 两种for循环 |
throw | 抛异常 |
throws | 声明异常 |
short | 短整型 |
native | 由宿主系统实现的方法 |
synchronized | 对线程而言是原子的方法或代码块 |
strictfp | 严格模式 |
transient | 瞬态 |
volatile | 允许多线程访问 |
assert | 查找内部错误 |
abstract | 抽象 |
标识符
自己定义的内容,如类名,变量名,方法名。
- 命名规则:
- 可以含字母(区分大小写),数字,下划线和美元符。
- 不能数字开头
- 不能是关键字
- 命名规范:
- 类名每个单词首字母大写。
- 变量名和方法名首个单词首字母小写,其余单词首字母大写。
- 常量名习惯用全大写单词与下划线组合书写
- 所有名称要求见名知义。
常量
- 指固定不变的数据。具体有:
- 整型常量
- 浮点型常量
- 字符常量
- 字符串常量
- 布尔常量
- 空常量
变量与数据类型
- 可以变化的量成为变量,一般是一个字母或者一个单词或者几个单词组成。变量一次只能保存(代表或者指向)一个数据,且数据类型必须是明确的。
- 数据类型:四类八种
- 四类:整数、小数、布尔、字符;
- 八种:字节、短整型、整型、长整型、单精度浮点、双精度浮点、字符、布尔。
- 字符串可以理解为常量,但类型是引用类型,不是基本类型。
- 字符型占用两个字节,但在utf-8编码下,文件中的一个字符占用3个字节。
- 变量名称:在同一个大括号范围内,变量的名字不可以相同。
- 变量赋值:定义的变量,不赋值不能使用。
- 给float和long类型的变量赋值,记得加F和L
- 超过了变量的作用域,就不能再使用变量了 作用域: 从定义变量的位置开始,一直到直接所属的大括号结束为止
- 同一条语句可以同时定义多个同类型的变量
- 如果给byte和short类型的变量赋值,记得不要超过该类型所表示的范围
数据类型转换
自动转换
- 基本类型的自动转换是范围小的可以向范围大的类型自动转换
- 引用类型没有自动转换,但是多态的特性可以使范围大的类型引用指向其子类的对象。
- byte/short/char类型发生运算的时候,会自动转换成int。
- char类型运算,字符表示的数值要参考ASCII码表。
强制转换
- 基本类型的强制转换和引用类型的强制转换格式类似。
- 基本类型的强制转换可能会损失精度。
- boolean类型不能强转。
- 一些运算符自带强转属性,如-=/+=/++/–/…
- 引用类型的强制转换只能在多态的情况下,在已知类型的情况下,强转成自身真正的类型。没有关联的两种类型发生强转会报错。
运算符
一元运算符
算术运算符:++、–
逻辑运算符:!
二元运算符
算术运算符:+、-、*、/、%
赋值运算符:=、+=、-=、/=、%=。
比较运算符:==、>、<、>=、<=、!=
逻辑运算符:&&、||
三元运算符
? :
运算符注意事项
- 整数运算只会得到整数。
- &&和||会短路。
- +可以计算,可以连接字符串。
- 整运算时,小于int类型的常量或者变量会提升为int。
- 常量整数运算的时候,如果参与运算的类型范围比int小,编译器会确定结果是否满足结果类型的范围,如果没有超过范围可以自动赋值给小范围的结果,这种现象有个约定名字称作常量优化机制。具体体现为:
byte b1=1;
byte b2=2;
byte b3=1 + 2; //
byte b4=b1 + b2;// 编译报错
- 字符串拼接运算,如果变量参与运算,会在常量区生成新的区域。两个字符串常量拼接,如果结果已在常量区存在,则直接引用。
- 字符串直接赋值,会在常量区寻找,如果没有再生成新的区域,有的话直接引用。
- 用new生成的字符串,在堆区。
String s1 = "ab";
String news1 = new String("ab");
String s2 = "abc";
String s3 = "a" + "b";
String s4 = s1 + "c";
s1 == s3; //true
s2 == s4; //false
s1 == news1 //false
流程控制
判断
- if
- if…else
- if…else if…else…
- if可以与三元运算符互换。
选择
- switch…case…break…default…
- 穿透性,没有break,会直接往下执行。
- 常与枚举配合
循环
- for
- while
- do…while
- break与continue
- 死循环与嵌套
方法
####方法的格式
修饰符 返回值类型 方法名(参数列表){
//代码省略...
return 结果;
}
构造方法
一种特殊的方法。后边介绍。
重载与重写
-
方法签名:
-
重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
-
重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
引用类型作为方法参数
- 方法的参数为基本类型时,传递的是数据值.
- 方法的参数为引用类型时,传递的是地址值.
可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
其实这个书写完全等价与
修饰符 返回值类型 方法名(参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。
tips: 上述add方法在同一个类中,只能存在一个。因为会发生调用的不确定性
注意:如果在方法书写时,这个方法拥有多参数,参数中包含可变参数,可变参数一定要写在参数列表的末尾位置。
注意事项
- 方法参数和返回值可以是任意类型(基本类型或者引用类型)。
System.out,print()方法,打印基本数据类型会直接打印,打印引用数据类型会调用该类的toString方法,如果没有重写,toString()方法默认打印地址值。其中数组中有个特殊现象,就是char[],因为char[]可以当作字符串,所以直接打印字符串和字符数组都是直接打印内容,其他数组则需要遍历,否则打印地址值。
数组
一维数组定义
- 数组存储的数据类型[] 数组名字 = new 数组存储的数据类型[长度];
- 数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3…};
- 数据类型[] 数组名 = {元素1,元素2,元素3…};
数组特点:
- 长度不可变。
- 数据可以是基本类型,可以是引用类型。
- 数据类型一旦确定,不能更改(多态除外)。
数组遍历
for循环,注意越界异常。
类与对象
类与对象的关系
- 类是对一类事物的描述,是抽象的。
- 对象是一类事物的实例,是具体的。
- 类是对象的模板,对象是类的实体(实例)。
####成员变量与成员方法
- 属于类的成员,成员方法没有static修饰
成员变量与局部变量区别:
- 在类中的位置不同
重点
- 成员变量:类中,方法外
- 局部变量:方法中或者方法声明上(形式参数)
- 作用范围不一样
重点
- 成员变量:类中
- 局部变量:方法中
- 初始化值的不同
重点
- 成员变量:有默认值
- 局部变量:没有默认值。必须先定义,赋值,最后使用
- 在内存中的位置不同
了解
- 成员变量:堆内存
- 局部变量:栈内存
- 生命周期不同
了解
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
匿名对象
没有变量名的对象,只用一次的对象,创建后马上使用,用完即不再需要。
包装类
基本类型数据不能作为泛型,为了解决这个问题,jdk提供了包装类可以自动装箱和自动拆箱。每个基本类型都可以转换成对应的包装类。
- 装箱:从基本类型转换为对应的包装类对象。
- 拆箱:从包装类对象转换为对应的基本类型。
包装类还有一个作用就是可以调用方法进行类型转换,主要是把字符串转换成基本类型。
static关键字
概述
关于 static
关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。
修饰成员变量
当 static
修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
静态方法
当static
修饰成员方法时,该方法称为类方法 。静态方法在声明中有static
,建议使用类名来调用,而不需要创建类的对象。调用方式非常简单。
- 静态方法调用的注意事项:
- 静态方法可以直接访问类变量和静态方法。
- 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
- 静态方法中,不能使用this关键字。
小贴士:静态方法只能访问静态成员。
静态成员的调用
被static修饰的成员可以并且建议通过类名直接访问。虽然也可以通过对象名访问静态成员,原因即多个对象均属于一个类,共享使用同一个静态成员,但是不建议,会出现警告信息。
静态原理
static
修饰的内容:
- 是随着类的加载而加载的,且只加载一次。
- 存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。
- 它优先于对象存在,所以,可以被所有对象共享。
静态代码块
定义在成员位置,使用static修饰的代码块{ }。
- 位置:类中方法外。
- 执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
总结
static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。
final
final: 不可改变。可以用于修饰类、方法和变量。
- 类:被修饰的类,不能被继承。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,不能被重新赋值。
- 局部变量——引用类型
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改,
内部类
- 成员内部类 :定义在类中方法外的类。
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须要建立内部类的对象。
内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。
比如,Person$Heart.class
- 匿名内部类 :是内部类的简化写法。它的本质是一个
带具体实现的
父类或者父接口的
匿名的
子类对象。
Object类
#####概述
java.lang.Object
类是Java语言中的根类,即所有类的父类。如果一个类没有特别指定父类, 那么默认则继承自Object类。
方法介绍
public String toString()
:返回该对象的字符串表示。
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。
调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”默认方式是比较两个对象的地址值。不然需要重写来满足自己的需求。
在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
public static boolean equals(Object a, Object b)
:判断两个对象是否相等。
封装
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
成员的权限
- public:公共的。
- protected:受保护的
- default:默认的
- private:私有的
public | protected | default(空的) | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
this
this代表所在类的当前对象的引用(地址值),即对象自己的引用。
记住 :方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。
构造方法
小贴士:无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。
- 如果你不提供构造方法,系统会给出无参数构造方法。
- 如果你提供了构造方法,系统将不再提供无参数构造方法。
- 构造方法是可以重载的,既可以定义参数,也可以不定义参数。
javaBean
JavaBean
是 Java语言编写类的一种标准规范。符合JavaBean
的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的set
和get
方法。
public class ClassName{
//成员变量
//构造方法
//无参构造方法【必须】
//有参构造方法【建议】
//成员方法
//getXxx()
//setXxx()
}
继承
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。
关于继承如下 3 点请记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。(以后介绍)。
super和this
-
super :代表父类的存储空间标识(可以理解为父亲的引用)。
-
this :代表当前对象的引用(谁调用就代表谁)。
继承的特点
成员变量
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super
关键字,修饰父类成员变量,访问本类成员变量可以直接访问,或加this
关键字,一般不加 。
成员方法——重写
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
- 方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
如果子类重写了父类方法,又想使用父类的原始方法,一样通过super
关键字进行调用即可。
构造方法
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个
super()
,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。 - 在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。
注意事项
-
子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
private(本类中) < (default 默认)(同一个包) < protected(本类和子类) < public(当前项目中)
-
子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
-
只有单继承。顶层父类是Object,所有类默认继承,是为超类。
抽象类
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
- 抽象方法 : 没有方法体的方法。
- 抽象类:包含抽象方法的类。
使用abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
- 如果一个类包含抽象方法,那么该类必须是抽象类。
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
- 重写抽象方法,也叫实现方法。
- 注意事项:
-
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
-
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
多态
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。
接口
概述
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。
接口的定义,它与定义类方式相似,但是使用 interface
关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
引用数据类型:数组,类,接口,Lamda。
接口的使用,它不能创建对象,但是可以被实现(implements
,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
抽象方法
抽象方法:使用abstract
关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
默认方法
默认方法:使用 default
修饰,不可省略,供子类调用或者子类重写。
可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。
接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。
静态方法
静态方法:使用 static
修饰,供接口直接调用。
静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。
接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
私有方法
私有方法:使用 private
修饰,供接口中的默认方法或者静态方法调用。
- 私有方法:只有默认方法可以调用。
- 私有静态方法:默认方法和静态方法可以调用。
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。
优先级
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。
多实现、多继承
#####多实现
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
非抽象子类实现接口:
- 必须重写接口中所有抽象方法。
- 继承了接口的默认方法,即可以直接调用,也可以重写。
#####多继承
一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends
关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。
小贴士:
子接口重写默认方法时,default关键字可以保留。
子类重写默认方法时,default关键字不可以保留。
其他成员的特点
- 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
- 接口中,没有构造方法,不能创建对象。
- 接口中,没有静态代码块。
JDK的常用api
Scanner
所属包:java.util.Scanner;
基本使用方法举例:
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
Random
所属包:java.util.Random;
基本使用方法:
//1. 导包
import java.util.Random;
public class Demo01_Random {
public static void main(String[] args) {
//2. 创建键盘录入数据的对象
Random r = new Random();
for(int i = 0; i < 3; i++){
//3. 随机生成一个数据(0-9)
int number = r.nextInt(10);
//4. 输出数据
System.out.println("number:"+ number);
}
}
}
ArrayList
所属包:java.util.ArrayList;
概念:属于集合的一种,可以看成长度可变的数组对象。支持泛型。
常用方法:
-
public boolean add(E e)
:将指定的元素添加到此集合的尾部。 -
public E remove(int index)
:移除此集合中指定位置上的元素。返回被删除的元素。 -
public E get(int index)
:返回此集合中指定位置上的元素。返回获取的元素。 -
public int size()
:返回此集合中的元素数。遍历集合时,可以控制索引范围,防止越界。
List自带toString方法,可以直接打印。
String
所属包:java.lang.String;
- String类是不可变的。
- 以赋值的形式创建字符串,可以被共享。节省内存。
- 字符串的本质是字符数组。所以字符数组可以直接打印内容而不是地址。
- 用关键字new出的String对象在堆区。
- 比较内容是否相等,用方法,而不用==号。
####Arrays
所属包:java.util.Arrays;
此类包含用来操作数组的各种方法,比如排序和搜索等。其所有方法均为静态方法,调用起来非常简单。
Math
所属包:java.lang.Math;
包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。其中都是静态方法。
日期类
Date
public Date()
:分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。public Date(long date)
:分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。public long getTime()
把日期对象转换成对应的时间毫秒值。
简单来说:使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定long类型的构造参数,可以自定义毫秒时刻。
DateFormat
java.text.DateFormat
是日期/时间格式化子类的抽象类。可以在Date对象与String对象之间进行来回转换。
常用的子类java.text.SimpleDateFormat
。
public SimpleDateFormat(String pattern)
:用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数pattern是一个字符串,代表日期时间的自定义格式。如"yyyy-MM-dd HH:mm:ss"
标识字母(区分大小写) | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
public String format(Date date)
:将Date对象格式化为字符串。public Date parse(String source)
:将字符串解析为Date对象。
Calendar
Calendar为抽象类,Calendar类在创建对象时并非直接创建,而是通过静态方法创建,返回子类对象:
public static Calendar getInstance()
:使用默认时区和语言环境获得一个日历。
常用方法:
public int get(int field)
:返回给定日历字段的值。public void set(int field, int value)
:将给定的日历字段设置为给定值。public abstract void add(int field, int amount)
:根据日历的规则,为给定的日历字段添加或减去指定的时间量。public Date getTime()
:返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月(从0开始,可以+1使用) |
DAY_OF_MONTH | 月中的天(几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(周几,周日为1,可以-1使用) |
System
java.lang.System
类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作。
public static long currentTimeMillis()
:返回以毫秒为单位的当前时间。public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将数组中指定的数据拷贝到另一个数组中。- src 原数组
- int 原数组索引起始位置
- dest 目标数组
- destPos 目标数组索引起始位置
- length 复制的元素个数
StringBuilder
-
public StringBuilder()
:构造一个空的StringBuilder容器。 -
public StringBuilder(String str)
:构造一个StringBuilder容器,并将字符串添加进去。 -
public StringBuilder append(...)
:添加任意类型数据的字符串形式,并返回当前对象自身。append方法具有多种重载形式,可以接收任意类型的参数。任何数据作为参数都会将对应的字符串内容添加到StringBuilder中。 -
public String toString()
:将当前StringBuilder对象转换为String对象。
##JAVA 高级
集合
概述
集合是用来存储数据的容器。数组也是容器,但是数组长度不可变。且一个数组只能存储一种类型的数据。集合只能存储引用类型数据,但基本类型可以通过包装类进行存储。所以集合功能比数据强大。
- 集合分为单列集合和双列集合。
单列集合
#####Collection
- Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是
java.util.List
和java.util.Set
。其中,List
的特点是元素有序、元素可重复。Set
的特点是元素无序,而且不可重复。List
接口的主要实现类有java.util.ArrayList
和java.util.LinkedList
,Set
接口的主要实现类有java.util.HashSet
和java.util.TreeSet
。
单列集合的公共方法
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(E e)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中。
List
List接口特点:
- 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
- 集合中可以有重复的元素,通过元素的contains方法,来比较是否为重复的元素。
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
ArrayList
java.util.ArrayList
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合。
LinkedList
java.util.LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList是一个双向链表。
Set
与List
接口不同的是,Set
接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
HashSet
java.util.HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet
底层的实现其实是一个java.util.HashMap
支持。
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
- 在HashSet下面有一个子类
java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。可以让元素有序,但一般不使用。
#####Collections
-
java.utils.Collections
是集合工具类,用来对集合进行操作。部分方法如下: -
public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中添加一些元素。 -
public static void shuffle(List<?> list) 打乱顺序
:打乱集合顺序。 -
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。 -
public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
比较器
-
集合中的sort方法是用默认规则进行比较,默认增序,是采用Comparable。
-
Comparator可能实现比较,通过实现这个接口并实现compare方法。
-
public int compare(String o1, String o2)
:比较其两个参数的顺序。两个对象比较的结果有三种:大于,等于,小于。
如果要按照升序排序,
则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)
如果要按照降序排序
则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)前减后升序
-
-
两种比较器的区别:
Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
迭代器Iterator
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator
。Iterator
接口也是Java集合中的一员,但它与Collection
、Map
接口有所不同,Collection
接口与Map
接口主要用于存储元素,而Iterator
主要用于迭代访问(即遍历)Collection
中的元素,因此Iterator
对象也被称为迭代器。
public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的。
迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
Iterator接口的常用方法如下:
public E next()
:返回迭代的下一个元素。public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。
增强for循环的底层也是迭代器。增强for循环被叫做语法糖。
双列集合
Map
Map
中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
- HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
- LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
Map接口中定义了很多方法,常用的如下:
-
public V put(K key, V value)
: 把指定的键与指定的值添加到Map集合中。使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
-
public V remove(Object key)
: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。 -
public V get(Object key)
根据指定的键,在Map集合中获取对应的值。 -
public Set<K> keySet()
: 获取Map集合中所有的键,存储到Set集合中。 -
public Set<Map.Entry<K,V>> entrySet()
: 获取到Map集合中所有的键值对对象的集合(Set集合)。public K getKey()
:获取Entry对象中的键。public V getValue()
:获取Entry对象中的值。
Map的遍历:
键找值方式:即通过元素中的键,获取键所对应的值
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
- Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map实例。新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。
例子:
public class HelloJDK9 {
public static void main(String[] args) {
Set<String> str1=Set.of("a","b","c");
//str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合
System.out.println(str1);
Map<String,Integer> str2=Map.of("a",1,"b",2);
System.out.println(str2);
List<String> str3=List.of("a","b");
System.out.println(str3);
}
}
需要注意以下两点:
1:of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等待;
2:返回的集合是不可变的;
数据结构
栈
栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
- 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
- 栈的入口、出口的都是栈的顶端位置。
- 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
- 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
队列
队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
- 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
- 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。
数组
数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。
查找元素快:通过索引,可以快速访问指定位置的元素。
增删元素慢
链表
链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
查找元素慢,增删元素快。
- 单向链表
- 双向链表
树
二叉树、红黑树等等
泛型
概述
- 泛型:可以在类或方法中预支地使用未知的类型。
定义泛型类
修饰符 class 类名<代表泛型的变量> { }
泛型方法
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
泛型接口
修饰符 interface接口名<代表泛型的变量> { }
通配符高级使用----受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
- 格式:
类型名称 <? super 类 > 对象名称
- 意义:
只能接收该类型及其父类型
异常
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
####异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable
,其下有两个子类:java.lang.Error
与java.lang.Exception
,平常所说的异常指java.lang.Exception
。
- ps:Throwable下不是只有Error和Exception两个类。
Exception—>runtimeExcepiton
- 异常分为编译时异常和运行时异常。运行时异常都继承来自Runtime类。
异常的处理
抛出 throw
throw new NullPointerException("要访问的arr数组不存在");
throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
声明 throws
关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
捕获 try…catch…finally
捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
//多个异常的情况
//注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
try{
编写可能会出现异常的代码
}catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获.
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型B e){ 当try中出现B类型异常,就用该catch来捕获.
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
- finally:
代码无论异常是否发生,都需要执行。
PS:当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。
自定义异常
异常类如何定义:
- 自定义一个编译期异常: 自定义类 并继承于
java.lang.Exception
。 - 自定义一个运行时期的异常类:自定义类 并继承于
java.lang.RuntimeException
。
注意事项
- 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
- 如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集。
- 父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
- 当多异常处理时,捕获处理,前边的类不能是后边类的父类
- 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
- 如果finally有return语句,永远返回finally中的结果,避免该情况.
多线程
进程与线程的区别
- 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
- 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
注意:
- 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。
- Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
- 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
线程调度:
计算机通常只有一个CPU时,在任意时刻只能执行一条计算机指令,每一个进程只有获得CPU的使用权才能执行指令。所谓多进程并发运行,从宏观上看,其实是各个进程轮流获得CPU的使用权,分别执行各自的任务。那么,在可运行池中,会有多个线程处于就绪状态等到CPU,JVM就负责了线程的调度。JVM采用的是抢占式调度,没有采用分时调度,因此可以能造成多线程执行结果的的随机性。
创建线程
Java使用java.lang.Thread
类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建并启动多线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
- 创建线程的另一种方式
采用java.lang.Runnable
也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调用线程对象的start()方法来启动线程。
Thread和Runnable的区别
总结:
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
线程同步
同步代码块
同步代码块:synchronized
关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
- 锁对象 可以是任意类型。
- 多个线程对象 要使用同一把锁。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。
同步方法
- 同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
同步锁
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock()
:加同步锁。public void unlock()
:释放同步锁。
线程状态
等待唤醒机制
什么是等待唤醒机制
这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。
就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
线程池
Java里面线程池的顶级接口是java.util.concurrent.Executor
,但是严格意义上讲Executor
并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService
。
Executors类中有个创建线程池的方法如下:
public static ExecutorService newFixedThreadPool(int nThreads)
:返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)
获取到了一个线程池ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:
-
public Future<?> submit(Runnable task)
:获取线程池中的某一个线程对象,并执行Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
使用线程池中线程对象的步骤:
- 创建线程池对象。
- 创建Runnable接口子类对象。(task)
- 提交Runnable接口子类对象。(take task)
- 关闭线程池(一般不做)。
###Lambda
####标准格式
Lambda表达式的标准格式为:
(参数类型 参数名称) -> { 代码语句 }
格式说明:
- 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
->
是新引入的语法格式,代表指向动作。- 大括号内的语法与传统方法体要求基本一致。
省略规则
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
使用前提
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的Runnable
、Comparator
接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。 - 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
IO流与File
File类
java.io.File
类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
构造方法
public File(String pathname)
:通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。public File(String parent, String child)
:从父路径名字符串和子路径名字符串创建新的 File实例。public File(File parent, String child)
:从父抽象路径名和子路径名字符串创建新的 File实例。
一个File对象代表硬盘中实际存在的一个文件或者目录。
无论该路径下是否存在文件或者目录,都不影响File对象的创建。
常用方法
-
public String getAbsolutePath()
:返回此File的绝对路径名字符串。 -
public String getPath()
:将此File转换为路径名字符串。 -
public String getName()
:返回由此File表示的文件或目录的名称。 -
public long length()
:返回由此File表示的文件的长度(字节)。绝对路径:从盘符开始的路径,这是一个完整的路径。
相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。
-
public boolean exists()
:此File表示的文件或目录是否实际存在。 -
public boolean isDirectory()
:此File表示的是否为目录。 -
public boolean isFile()
:此File表示的是否为文件。 -
public boolean createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。 -
public boolean delete()
:删除由此File表示的文件或目录。 -
public boolean mkdir()
:创建由此File表示的目录。 -
public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录。 -
public String[] list()
:返回一个String数组,表示该File目录中的所有子文件或目录。 -
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。
递归
递归:指在当前方法内调用自己的这种现象。
递归一定要有条件限定,保证递归能够停止下来,次数不要太多,否则会发生栈内存溢出。
IO流
根据数据的流向分为:输入流和输出流。
- 输入流 :把数据从
其他设备
上读取到内存
中的流。 - 输出流 :把数据从
内存
中写出到其他设备
上的流。
格局数据的类型分为:字节流和字符流。
- 字节流 :以字节为单位,读写数据的流。
- 字符流 :以字符为单位,读写数据的流。
顶层父类:
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
缓冲流,也叫高效流,是对4个基本的FileXxx
流的增强,所以也是4个流,按照数据类型分类:
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
- 字符缓冲流:
BufferedReader
,BufferedWriter
转换流:
java.io.InputStreamReader
,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
java.io.OutputStreamWriter
,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集讲字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。
序列化流:
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
写出对象方法
public final void writeObject (Object obj)
: 将指定的对象写出。
一个对象要想序列化,必须满足两个条件:
- 该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。 - 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream
读取对象的方法:
public final Object readObject ()
: 读取一个对象。
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException
异常。
**当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。**发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
函数式接口
方法引用
Stream流
网络
C/S和B/S
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。
TCP和UDP
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。