赞
踩
#java开发环境
1、什么jdk?
java development kit
JRE
java runtime enviroment
2、安装jdk
官网下载:jdk11.0.9
A、新建目录:D:\javadev
B、安装:D:\javadev\Java\jdk-11.0.9
C、配置环境变量:
JAVA_HOME:D:\javadev\Java\jdk-11.0.9
PATH:%JAVA_HOME%\bin
D、测试,cmd窗口
java -version
javac
#编码规范
类名的编码规则:
语法规则,如果不符合语法规则就不能通过编译
1、类名首字母:字母+下划线+美元符号
2、其余字母:字母+下划线+美元符号+数字
3、不能是java关键字
类名的编码规范:
编码习惯,良好的编码习惯应该发扬光大
1、类名的每个单词的首字母大写:匈牙利命名法
2、类名应该有意义,望文知义
3、类名通常是名词
4、括号应该缩进,括号与对应的结构顶端对齐
变量命名规则:
首字母:字母 下划线 美元符号
其余字母:字母 下划线 美元符号 数字
变量命名规范:
首个单词都小写,后续单词首字母大写(驼峰命名法)
简短有意义
不能是关键字
java的特性
1、跨平台
由于Java程序运行在JVM上而非直接运行在操作系统上面,所有的主流操作系统都可以从java官网免费下载安装JRE,JRE内置JVM,从而实现了java程序的平台无关性。
2、面向对象:OOP
Java吸取了C++面向对象的概念,将数据封装于类中,利用类的优点,实现了程序的简洁性和便于维护性。类的封装性、继承性等有关对象的特性,使程序代码只需一次编译,然后通过上述特性反复利用
3、安全性
Java的编程类似C++,Java舍弃了C++的指针对存储器地址的直接操作,程序运行时,内存由操作系统分配,这样可以避免病毒通过指针侵入系统
4、分布式
Java建立在扩展TCP/IP网络平台上。库函数提供了用HTTP和FTP协议传送和接受信息的方法。这使得程序员使用网络上的文件和使用本机文件一样容易
5、健壮性
Java致力于检查程序在编译和运行时的错误。类型检查帮助检查出许多开发早期出现的错误。Java自己操纵内存减少了内存出错的可能性。Java还实现了真数组,避免了覆盖数据的可能,这些功能特征…
变量
1、程序中的数据存储在哪里?
计算机的内存中,怎么存进去的?–>通过变量来存储
2、什么是变量?
变量是计算机存储空间的表示
3、使用变量的步骤?
A、声明
数据类型 变量名;
String name;
B、初始化
变量名 = 值;
name = “张三”;
C、使用
System.out.println(“姓名:”+name);
4、变量使用的步骤的简化:
int age = 19;
String sex = “男”;
数据类型
1、java数据类型的分类:
基本数据类型: 包装类 占用空间大小(字节) 范围
byte 字节 Byte 1 -128~127
short 短整形 Short 2 -32768~32767
int 常规整形 Integer
long 长整形 Long
float 单精度浮点型 Float
double 双精度浮点型 Double
char 字符型 Character
boolean 布尔型 Boolean
引用数据类型: String、System、Date、HelloJava、8种基本类型的包装类型等等
2、数据类型的默认值
byte、short、int、long默认值:0
float、double默认值:0.0
char默认值:
boolean默认值:false
运算符
1、算术运算符
+ - * / % ++ -- =
前置++:先将变量自增,然后再参与表达式运算
后置++:先取出变量的值参与表达式运算,然后再自增
2、关系运算符
大于
< 小于
= 大于等于
<= 小于等于
== 等于
!= 不等于
3、数据动态录入的工具类:
java.util.Scanner;
使用步骤:
A、导包:import java.util.Scanner;
B、创建对象:Scanner input = new Scanner(System.in);
C、调用对象input的相关方法接收键盘的录入
字符串:input.next();
整数: input.nextInt();
浮点数: inputnextFloat();
4、位运算符
<<:左移,M<M乘以2的n次方
>>:右移,M>>n–>M除以2的n次方
>>>:无符号右移
~:取反
位与:&
位或:|
异或:^
位的与运算:& 十进制
00000011 3
00001001 9
00000001 1
位或:|
00000011
00001001
00001011 11
异或:^
00000011
00001001
00001010 10
<<:左移 左移2
00000011 3 00001100 12 = 3*4
5、逻辑运算符
逻辑与:&
短路与:&&
逻辑或:|
短路或:||
逻辑非:!
流程控制结构
分支结构:
if分支结构
switch分支结构
循环结构:
for
while
do-while
1、分支结构:
//简单的分支结构
if(条件表达式){
//代码块1
}
//if-else分支结构
if(条件表达式){
//代码块1
}else{
//代码块2
}
//多重条件的if-else分支结构
if(条件表达式1){
//代码块1
}else if(条件表达式2){
//代码块2
}else if(条件表达式3){
//代码块3
}else{
//代码块n
}
//嵌套的if-else结构
if(条件表达式){
if(条件表达式){
//代码块1
}else{
//代码块2
}
}else{
}
2、switch分支结构
A、switch的条件表达式的数据类型:byte、short、int、char、enum、String(jdk7)
B、break用于跳出switch结构,一般不要省略
C、case和default的顺序
D、所有的switch结构都可以转换成if-else,但是反之则不一定
3、循环结构:
for循环结构语法:
for(表达式1;表达式2;表达式3){
//循环体
}
特点:
循环次数确定
先判断,再循环,如果条件不满足,有可能一次循环都不执行
whie循环结构语法:
while(条件表达式){
//循环体
}
特点:
循环次数不确定
先判断,再循环,如果条件不满足,有可能一次循环都不执行
do-while循环语法:
do{
//循环体
}while(条件判断);
特点:
循环次数也是不确定
先执行,再判断,如果循环条件不满足,也至少执行一次
字符串的比较
字符串的比较:
使用==比较的是字符串变量的存储地址是否相等。
如果要比较两个字符串的内容是否相等,必须使用其方法:equals()
伪随机数生成
java生成伪随机数方式一:使用Math数学类
Math.random():
生成的随机数是:大于等于0并且小于1的浮点数
random.nextInt(10):
生成的随机数是:大于等于0并且小于10
数组
数组:数组就是一种能同时存储多个相同数据类型数据的变量。
变量的使用步骤:
1、声明变量
2、给变量赋值
3、使用变量
数组的使用步骤:
1、声明数组
数据类型 []数组名称; 或者 数据类型 数组名称[];
int []scores;
2、分配空间
数组名称=new 数据类型[大小];
scores = new int[24];
3、给数组赋值
数组名[index]=值;
scores[0] = 90;
scores[1] = 88;
…
scores[23] = 67;
4、使用数组
scores[index]使用
数组使用的合并步骤:
1、声明和分配空间合并:
int []scores = new int[24];
2、声明+分配空间+赋值合并
int []scores = new int[]{99,88,78};
int []scores = {99,88,78};
数组的特性:
1、数组的下标总是从0开始
2、数组的最后一个元素的下标总是等于数组的大小-1
3、数组一旦分配空间,元素就有了初始值,这些初始值是数组数据类型的默认值
二维及多维数组:
二维数组的使用:
1、声明二维数组
int [][]scores;
2、分配空间
scores = new int[3][5];
3、赋值
scores[0][0] = 99;
scores[0][1] = 99;
排序算法
各种常用排序算法:
冒泡排序、选择排序、插入排序、快速排序、二分排序
希尔排序、堆排序、归并排序
冒泡排序
口诀:
n个元素来排序
相邻两两来比较
外层循环n-1
内层循环n-1-i
递归
什么是递归?
程序调用自身的编程技巧称为递归( recursion)。递归做为一种
算法在程序设计语言中广泛应用。一个过程或函数在其定义或说
明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问
题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略
只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地
减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无
限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
构成递归需具备的条件:
递归的缺点:
递归算法解题相对常用的算法如普通循环等,运行效率较低。
因此,应该尽量避免使用递归,除非没有更好的算法或者某
种特定情况,递归更为适合的时候。在递归调用的过程当中
系统为每一层的返回点、局部量等开辟了栈来存储。递归次
数过多容易造成栈溢出等。
面向对象编程:
面向过程:将解决复杂问题的步骤拆解为一个个方法或者函数,这种编程方法论称为面向过程。
面向对象:将人们认识现实世界的思维映射到软件工程中,这种编程的方法论称为面向对象编程。
人们是这样认识现实世界的:
1、将现实世界的所有东西进行归类
2、现实世界一切皆对象
Java是一门面向对象的编程语言,跟我们认识现实世界的相似,总是遵从如下步骤:
1、抽象出类
2、识别出类所具有的特征和行为
3、再从类中创建出该类别的对象
什么是类?
类是具有相同属性和方法的对象的抽象,类是模板。
什么是对象?
对象是类的个体。
什么是类的属性和方法?
属性就是类的特征,定义了该类的对象所共同具有的特性。
方法就是类的行为,定义了该类的对象所共同具有的动态特性。
成员变量:
属性、对象变量、实例变量、全局变量
成员方法:
对象方法、实例方法
方法
方法的概念:
方法是类定义的所有对象的行为
方法的语法:
访问修饰符 返回类型 方法名(参数列表){
//方法体
}
比如:
public void show(){
//方法体代码
}
访问修饰符:用于修饰方法能在什么地方被调用
public 公共的
protected 受保护的
默认
private 私有的
返回类型:
如果有返回类型,则方法体必须用return返回相应类型的数据
如果没有返回类型,则使用void
参数列表:
参数可以有0到多个
定义:数据类型 变量名,称为:形式参数
jdk5开始,支持可变参数,使用…
方法的调用:
1、同一个类的方法调用,直接调用
2、不同类的方法调用,不能直接调用,必须以:对象名.方法名() 方式调用
方法重载:
概念:同一个类的多个同名方法,由于其参数列表不同,形成方法重载。
条件:重载与返回类型、修饰符无关
#类的对象创建方式及构造函数:
类是模板,使用类的对象必须通过new关键字类创建,语法:
类名 引用名 = new 类的构造(实参);
构造方法:
概念:构造方法是一种特殊的方法,其名称与类名相同,并且没有返回类型也没有void修饰
作用:构造方法用来进行对象的初始化
默认构造方法:
如果没有显式定义类的构造,则系统会在编译时自动提供一个默认的构造器。
如果显式定义了类的构造,则系统不会生成默认的构造。
构造函数可以重载。
this关键字:
指代当前对象,通过this.属性名来引用对象的成员变量
构造函数重载:
同一个类具有多个构造函数,这些构造函数的参数列表不同。
绑定机制:
属于:编译器绑定(前期绑定/静态绑定)
参数传参机制
Java方法参数传递机制:
1、基本数据类型的参数传递
按值传递
2、引用数据类型的参数传递
按值传递
3、字符串参数的传递
按值传递
注意:字符串具有不变性,对字符串的任何的:截取、拼接等操作都导致创建新的字符串
变量的作用域
1、变量具有作用域,所谓作用域就是变量发生作用的范围
2、按照作用域划分,变量分为:
局部变量:
方法里面、参数、返回类型、代码块
作用域:在定义的花括号里面
成员变量:
类里面,与方法同级
作用域:在整个对象里面
类变量(静态变量):
类里面定义,但是要使用关键字:static
作用域:整个类的所有对象
线程级变量(ThreaLocal):
定义:基于ThreaLocal类来实现
作用域:本线程的所有类都能共享
OOP:面向对象编程
特性:封装、继承、多态
封装:
封装是将对象数据私有化,隐藏核心数据及实现细节,提供公共的安全的访问接口来访问。
1、为什么需要封装?
2、实现封装的步骤:
A、将属性私有化
B、给属性添加读写方法(getter/setter)
C、根据业务规则给方法添加访问控制代码
分析Dog和Penguin两个类:
1、狗狗和企鹅都是宠物,他们与宠物之间存在一种关系:is-a
2、存在大量相同的代码
为了代码重用,我们使用:继承
java的继承使用关键字:extends
增加一个父类:Pet,让Dog和Penguin继承
1、将子类中相同的属性提取到父类中
2、子类构造中,调用父类的构造进行初始化,通过关键字:super()
继承关系的细节:
1、父类看不见子类特有的属性和方法
2、子类可以重写(覆盖)父类的方法
3、子类调用父类的构造函数:super()
4、子类调用父类的方法:super.方法名()
父类不能被子类继承的东西:
1、私有的属性和方法不能被继承
2、构造函数不能被继承
什么是方法重写?
1、子类与父类具有同名的方法名,并且方法参数列表、返回类型都相同
2、子类方法的访问修饰符不能严于父类被重写方法
3、子类方法声明的异常不能宽于父类被重写方法
与方法重载对比学习
1、同一个的同名方法
2、这些方法的参数列表不同(个数、类型)
3、重载与返回值、访问修饰符无关
Java通过4个访问控制修饰符来控制对象、方法、变量的可访问访问:
public
protected
默认
private
宠物系统中企鹅的性别只能是:Q男\Q女
使用枚举来优化
Java中使用enum来定义枚举类型:
1、枚举enum是一种类型
2、用于定义能列举的有限的常量值
使用枚举优化企鹅的性别:
1、定义性别枚举类型
public enum SexEnum {
Q男,Q女
}
2、修改企鹅的代码
private SexEnum sex; //性别
public SexEnum getSex() {
return sex;
}
public void setSex(SexEnum sex) {
this.sex = sex;
}
分析Pet中的show()方法:
1、该方法必须存在,因为子类需要重写
2、但是该方法在父类的实现都被子类覆盖了,因此没有必要实现。
抽象方法:
1、被关键字:abstract修饰
2、没有方法体(没有实现)的方法
3、抽象方法所在的类必须是一个抽象类
抽象类:
1、被abstract修饰的类就是抽象类
2、抽象类不一定有抽象方法
3、抽象类不能被new(实例化)
继承的目的是为了代码复用,但是:
1、java的单根继承
2、能不用继承吗?
OOD:面向对象的设计
六大设计原则:
1、合成复用优于继承复用
2、迪米特法则
3、开闭原则
4、接口隔离原则
5、李氏特换换原则
6、依赖倒置原则
宠物系统除了宠物类之外,必须有主人类:
主人类:Master
属性:名称(name)
方法:
照顾狗狗:tackCare(Dog dog)
照顾企鹅:tackCare(Penguin penguin)
增加宠物子类去医院方法:
Dog:
toHospital()
Penguin:
toHospital()
引出一个问题:
1、宠物系统增加一个宠物子类:Cat
2、系统增加的代码:
实体类:Cat 属性:昵称、健康值、亲密度、品种 方法:show(),toHospital()
主人类:增加一个照顾猫咪的方法
思考:每增加一个新的宠物类,主人类Master就必须增加一个照顾新宠物的方法,违反了六大设计原则:
开闭原则(OCP)
开闭原则:
程序应该对扩展开放而对修改关闭。
设计的程序应该在不需要修改代码的情况下而具有扩展能力。
程序设计应该追求:
1、可扩展性
2、可维护性
3、健壮性
4、安全性
基于多态实现系统的可扩展性:
1、修改Master的tackCare方法,提升参数类型为父类
2、在父类Pet中定义抽象方法:toHospital,宠物子类重写该方法
什么是多态?
同一个父类型对象引用,指向不同子类对象时,对象表现出不同的形态。
实现多态的关键步骤:
1、方法参数父类化、抽象化、接口化–>面向接口编程(面向抽象编程)
2、子类父类之间存在方法重写
方法重载:
System.out.println(“张三”);
System.out.println(‘A’);
System.out.println(999);
System.out.println(true);
编译器多态:
方法绑定在编译器就绑定了,也称为:前期绑定/静态绑定
父类Pet的方法:
toHospital()
子类重写:
Dog、Penguin、Cat都重写了:toHospital
在Mster类中的:
public void tackCare(Pet pet) {
//发现狗狗生病,带狗狗去医院
if(pet.getHealth()<50) {
pet.toHospital();
}
}
问题:编译器能不能确定pet.getHealth()调用的是哪个宠物子类对象的方法?
答案:不能!因为具体的子类对象必须在程序运行时才能确定!
这就是运行时多态:
也称为:后期绑定/动态绑定
李氏替换原则:
派生类(子类)对象可以在程式中代替其基类(超类)对象,而不会改变基类原有的功能。
final关键字:
1、修饰变量–>常量
修饰成员变量,变量成为:常量,其值不可变。
比如:final double PI = 3.1415926;
常量名一般都大写,每个单词之间用下户线连接
2、修饰方法-->不可被重写
修饰方法,方法不能被覆盖
3、修饰类-->类不能被继承
比如,系统类:
String、Integer、Byte、Short、Long、Float、Double、Character、Boolean
static关键字:
1、修饰成员变量–>静态变量/类变量
访问成员变量:
类内部访问:直接访问
类外部访问:对象名.成员变量名
访问静态变量:
类内部访问:直接访问,注意成员
类外部访问:1、类名.静态变量名 2、对象名.静态变量名
初始化的时机:
静态变量初始化时机:类被加载的时候
成员变量的初始化时机:对象实例化的时候
2、修饰方法-->静态方法/类方法
访问成员方法:
类内部访问:直接调用
类外部访问:对象名.方法名()
访问静态方法:
类内部访问:直接调用,但是注意:静态方法不能访问成员变量和成员方法
3、修饰代码块-->静态块
调用的时机:类加载的时候就调用
4、修饰内部类
Object类:
是所有类的直接或者间接父类,定义了所有子类都能继承的方法。
定义的方法:
getClass()
hashCode()
equals(Object obj)
clone()
toString()
notify()
notifyAll()
wait()
wait(long timeoutMillis)
wait(long timeoutMillis, int nanos)
finalize()
重写:
equals()–对象比较方法
hashCode()–返回哈希值
一个类中可以定义有:
成员变量
静态变量
成员方法
静态方法
代码块
静态块
构造函数
以调试方式启动程序,观察程序代码运行的过程,得出结论:
0、类加载器加载字节码文件到jvm内存
1、静态变量的默认初始化
2、静态变量的显式初始化
3、执行静态块
4、执行成员变量的默认初始化
5、执行成员变量的显式初始化
6、执行代码块
7、执行构造函数
Maven是什么?
是一种将项目(jar、war、ear)视为对象来管理,管理其的构建、初始化、清理、打包、安装等等。
核心概念:
pom–项目对象模型
基于类似于GPS的定位技术,gps有经纬度,Maven有:groupId、artifactId、version
搭建Maven环境:
1、官网下载:https://apache.org/
2、安装:解压–>D:\javadev\apache-maven-3.6.1
3、配置环境变量:
MAVEN_HOME D:\javadev\apache-maven-3.6.1
path %MAVEN_HOME%\bin
4、建立、配置本地仓库
新建一个目录作为本地仓库:D:\javadev\006_maven_repository
编辑:conf\settings.xml文件,添加本地仓库配置:
D:/javadev/006_maven_repository
5、配置阿里的镜像仓库
alimaven
central
aliyun maven
http://maven.aliyun.com/nexus/content/repositories/central/
6、初始化
打开cmd窗口,输入
mvn -v
mvn help:system
异常体系
1、什么是异常?
程序在运行过程中出现的错误称为异常。
异常类型:
ArithmeticException 算术异常,除数为0
InputMismatchException 输入不匹配异常
java的异常机制:
try 用于监视可能出现异常的代码
catch 用于捕获发生的异常类型
finally 用于执行那些不管有没有异常出现都要执行的代码
throw 用于自动抛出异常
throws 用于方法声明可能抛出的异常类型
各种异常处理结构:
try{
//可能发生异常的代码
}catch(异常的类型 e){
//处理异常的代码
}
异常类的体系结构:
Throwable
Error
Exception
Checked Exception(检查异常):
应用程序必须处理:
1、try-catch
2、方法声明异常
Runtime Exception(运行时异常)
不强制程序处理
常见的检查异常:
SQLException、ClassNotFoundException、IOException
常见的运行时异常:
ArithmeticException、InputMismatchException、NullPointerException、
IndexOutOfBoundsException、ClassCastException
常见的异常结构执行情况:
try-catch-finally
1、没有异常发生:try–>finally
2、发生异常:
异常被捕获:try–>catch–>finally
异常没有被捕获:try–>finally,程序崩溃了
自定义异常类型:
写一个类继承于:Throwable、Exception、RuntimeException
/**
在异常处理结构中有return返回的情形:
public int method1(int a,int b) {
int num1 = 10;
try {
int c = a / b;
/*
* 执行return的时候,取出num1的值,赋给一个临时变量:temp
* 执行完finally后,方法返回,返回的值是temp的值
*/
return num1;
} catch (Exception e) {
System.out.println(“出错了!”);
return num1;
}finally {
num1++;
}
}
1、字符串是一种引用数据类型
2、字符串具有不变性
3、字符串常量对象和非常量对象的存储原理
4、字符串的比较:==和equals()的区别
字符串类:
1、是一个final类:不能被继承–>不可变类
2、常用方法:
charAt(int index) 返回给定索引的字符
length() 返回字符串的长度–字符的个数
concat(String str)将指定的字符串连接到该字符串的末尾。
contains(CharSequence s)当且仅当此字符串包含指定的char值序列时才返回true
endsWith(String suffix)测试此字符串是否以指定的后缀结尾
startsWith(String prefix)测试此字符串是否以指定的前缀开头。
split(String regex)将此字符串分割为给定的 regular expression的匹配
substring(int beginIndex)从给定的下标开始到结束,截取子串
substring(int beginIndex, int endIndex)左闭右开,截取子串
toLowerCase()转换成小写
toUpperCase()转换成大写
trim()剪去两边空格
indexOf(int ch)返回指定字符第一次出现的字符串内的索引
lastIndexOf(int ch)返回指定字符的最后一次出现的字符串中的索引
字符串缓存器类:
StringBuffer:线程安全的
StringBuilder:非线程安全的
1、什么是内部类?
定义在类中的类就是内部类。
2、分类:
普通内部类
局部内部类
静态内部类
匿名内部类
普通内部类:
位置:类的属性
在外面创建内部类对象:OuterClass.InnerClass innObj1 = new OuterClass().new InnerClass();
特点:
1、普通内部类能直接访问外部类的属性和方法
2、外部类通过创建内部类对象来访问其成员
局部内部类:
位置:在方法中
特点:
1、局部内部类定义在类的方法中
2、局部内部类不能被public修饰
3、局部内部类能直接访问外部类的成员
匿名内部类:
局部内部类实现接口的情况,只使用一次,可以不定义,直接创建接口的匿名对象。
//匿名内部类
new MyInterface() {
@Override
public void interMethod() {
System.out.println(“我是匿名内部类,我实现了接口的抽象方法!”);
}
}.interMethod();
静态内部类:
普通一个类的定义,不能使用static修饰符,但是内部类作为类的属性存在,属性是可以静态的,因此普通内部类也可以是静态的。
特点:
静态内部类访问外部类:
不能访问成员
能访问静态变量、方法
外部类访问静态内部类:
通过内部类实例访问其成员
通过类名访问其静态成员
内部类的可以曲线实现java的多继承。
抽象类和抽象方法:
1、被abstract修饰的类
2、抽象类不能被实例化
3、抽象类的子类必须实现抽象类的抽象方法,除非子类也是抽象类
4、抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
5、被abstract修饰的没有方法体的方法
6、抽象方法不能被final修饰
7、抽象类不能被final修饰
假如一个抽象类只有抽象方法,抽象类就可以定义成接口。
接口:
1、用interface定义的类型
2、接口能包含:抽象方法、常量
3、在1.8及后续版本中,接口的抽象方法可以有默认实现
理解接口:
1、接口代表一种能力,通过其定义的方法来体现
2、接口代表一种规范,通过名称和注释来体现
接口和抽象类的相同点:
1、都不能实例化
2、都是用来被继承或者实现
3、都能包含抽象方法
不同点
1、抽象类能包含构造方法,接口没有
2、抽象类能包含实现方法,接口不能,但1.8以后接口也可以包含默认实现的方法
3、抽象类是单根继承
4、接口可以多继承
变量:
存储空间的表示
数组:
存储多个相同数据类型的数据
数组的限制:
大小不可变
理解java集合的分类:
从版本划分:
旧版的:
jdk1.0
Stack、Hashtable、Vector
新版的:
jdk1.2
Collection
Map
是否线程安全:
线程安全的:
Stack、Hashtable、Vector
非线程安全的:
Collection
Map
新的集合框架组成:
接口:
存储单值数据:Collection
特性:无序的、可重复
子接口:Set、List
List:
特性:有序,可重复的
Set:
特性:无序,唯一
存储k-v键值对数据:Map
实现类:
List:
ArrayList
LinkedList
Set:
HashSet
TreeSet
Map:
HashMap
TreeSet
工具类:
Collections
数组列表:ArrayList
是一个可变大小的数组,实现了所有List接口的抽象方法。
可存储所有类型的数据,包括null
常用方法:
add(obj):在末尾添加
add(index,obj):指定位置添加
get(index):获取指定位置的元素
特性:
底层存储机制:
数组,存储空间连续,在随机访问,遍历性能比较高
在删除、插入的性能比较低
集合泛型:
ArrayList:限制了该集合只能添加Student类型的元素
eclipse中使用lombok的步骤:
1、pom文件引入lombok依赖
2、打开eclipse安装目录下的eclipse.ini,在最后加上一行:
-javaagent:D:\javadev\025_maven_repository\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar
2、注解的使用:
@Data 编译时生成属性的getter/setter方法
@NoArgsConstructor 默认构造函数
@AllArgsConstructor 全参构造
@ToString
链表列表:LinkedList
是一种双向链表
可存储所有类型的数据,包括null
常用方法;
add()
addFirst()
addLast()
特性:
底层存储:链表数据结构,在删除、添加操作性能比数组列表要高,但是在随机访问、遍历上要比较低
HashSet:
特性:元素唯一,无序的
存储原理:底层存储采取hash函数算法,海量数据中搜索的性能与小量数据搜索元素的性能相差无几
迭代器原理:
设计模式:迭代子模式
枚举原理(Enumeration):
在迭代器出现之前,java使用枚举类进行集合元素的遍历
TreeSet:
实现了:SortedSet
特点:有序的
Map接口的实现类:
HashMap:
无序的
key:唯一,null
value:null
常用方法;
get(key) 根据key获取映射的value
keySet() 获取所有的key
values() 获取所有的value
entrySet()获取所有的条目
底层存储原理:
1.7及以前:数组+链表
1.8及以后:数组+链表+红黑树
当链表长度大于8–>红黑树
当红黑树长度小于6–>链表
非线程安全的类:
ArrayList、LinkedList、HashSet、TreeSet、HashMap
集合的工具类:
Collections
提供了用于操作集合元素,提供线程安全支持等静态方法
提供集合的线程安全支持:synchronizedXXX
洗牌:Collections.shuffle(nameList);
排序:Collections.sort(nameList);
反转:Collections.reverse(nameList);
排序的map:SortedMap的实现类–>TreeMap
#对象的比较
自定义数据类型的比较,需要实现接口:Comparable
为自定义类型:Student实现Comparable,并实现接口的抽象方法:compareTo
在比较方法中实现比较逻辑:
@Override
public int compareTo(Student o) {
if(this.id>o.id)
return 1;
else if(this.id<o.id)
return -1;
else
return 0;
}
一、文件类File
File类
File类是用来操作文件和目录的类,但它不能操作文件中的数据。
public class File extends Object implements Serializable, Comparable
File类实现了Serializable、 Comparable,说明它是支持序列化和排序的。
File案例演示:
详见相关工程案例
@Test
public void test1() throws IOException {
//创建一个文件对象
File file = new File(“D:\file_home\doc\myfile.txt”);
//获取file的父文件
File parent = file.getParentFile();
//判断parent是否存在
if(!parent.exists()) {
parent.mkdirs();
}
if(!file.exists()) {
//如果文件不存在,则创建
file.createNewFile();
}
}
二、Java的IO流
Java用于处理输入输出的api:
阻塞的:java.io包
非阻塞的:java.nio包
IO,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。
Java 中是通过流处理IO 的,那么什么是流?
流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。
当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
一般来说关于流的特性有下面几点:
IO流分类
IO流主要的分类方式有以下3种:
字节流和字符流的其他区别:
3.节点流和处理流
节点流:直接操作数据读写的流类,比如FileInputStream
处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)
处理流和节点流应用了Java的装饰者设计模式。
在诸多处理流中,有一个非常重要,那就是缓冲流。
我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。
字节流:
InputStream 输入流基类
BufferedInputStream 缓存输入流
OutputStream 输出流基类
BufferedOutputStream 缓存的输出流
字符流:
Reader
Writer
字符流:
抽象类:
Reader 输入流抽象类
缓存的字符输入流类:BufferedReader
new BufferedReader(new FileReader(“foo.in”));
Writer 输出流抽象类
缓存的字符输出流类:
new BufferedWriter(new FileWriter(“foo.out”))
Java对象支持通过流进行传输,包括将对象存储到磁盘文件上,称为序列化;
将磁盘文件上的对象重新读取到内存中,称为反序列化。
支持序列化的类必须实现序列化接口:java.io.Serializable
对象的序列化需要用到:
ObjectInputStream
ObjectOutputStream
1、多任务处理
基于进程
基于线程
2、什么是进程?
一个应用程序表现为一个进程,进程之间独享自己的内存空间
3、什么是线程?
线程是进程的最小代码执行流,同一个进程的多个线程共享进程的内存空间。
cpu在线程之间的任务切换的开销要远远低于在进程之间的切换。
4、线程的创建方式
A、继承方式
写一个类继承于:java.lang.Thread
重写父类的run()方法
B、接口方式
写一个类实现接口:java.lang.Runnable
实现接口的run()方法
5、线程的生命周期的五个状态:
新建状态:new
就绪状态:start()
运行状态:获得cpu调度
阻塞状态:slee()、IO、wait
死亡状态:
6、线程的调度
sleep:休眠,属于Thread的静态方法
yield:当前线程让出cpu执行权,重新进入就绪状态
join:当前线程让出cpu执行权,直到join(加入)的线程执行完毕,属于Thread的静态方法
7、多线程的数据共享
多个线程访问同一个数据,会出现数据共享问题。
解决方法:
使用线程同步关键字:synchronized
1、修饰方法,同步方法
2、修饰代码块,同步块
1、存在可见性问题
阐述:jvm内存模型中,每个线程都有自己的工作内存,线程对变量的读取是:先从主存(共享内存)中读取变量值到自己的工作内存中,然后进行操作,最后再写入回主存。这样在自己的工作内存中读、写是对其他线程不可见的。
解决方法:使用volatile关键字修饰共享变量,让线程直接读写主存。
2、原子性问题
阐述:基本数据类型除long和double外的读写具备原子性保障。
类似:a+=2这样的操作不具备原子性,因为操作被分成:
a)取出a的值
b)计算a+2
c)将计算结果写入内存
在上述步骤之间很可能线程调度器中断,转向另一个任务(线程),这个线程可能修改这个域,造成结果错误,所以这个操作不是原子性的。
解决办法:
1、线程同步:关键字synchronized
修饰方法,同步方法
修饰代码块,同步块
2、并发包下的原子类:java.util.concurrent.atomic包
AtomicBoolean
AtomicInteger
AtomicLong
了解并发包下的原子类实现原子操作的原理:
基于:CAS原理实现,全称是:compare and swap–比较并交换
3、有序性问题
在多线程环境下,jvm会对代码进行重排序来优化程序,这样,在某些情况下,可能会导致程序问题。使用同步关键字synchronized能解决有序性问题。
概念辨析
1、volatile和synchronized的区别
2、什么是CAS?
使用锁时,线程获取锁是一种悲观锁策略,即假设每一次执行临界区代码都会产生冲突,所以当前线程获取到锁的时候同时也会阻塞其他线程获取该锁。而CAS操作(又称为无锁操作)是一种乐观锁策略,它假设所有线程访问共享资源的时候不会出现冲突,既然不会出现冲突自然而然就不会阻塞其他线程的操作。因此,线程就不会出现阻塞停顿的状态。那么,如果出现冲突了怎么办?无锁操作是使用CAS(compare and swap)又叫做比较交换来鉴别线程是否出现冲突,出现冲突就重试当前操作直到没有冲突为止。
CAS比较交换的过程可以通俗的理解为CAS(V,O,N),包含三个值分别为:V 内存地址存放的实际值;O 预期的值(旧值);N 更新的新值。当V和O相同时,也就是说旧值和内存中实际的值相同表明该值没有被其他线程更改过,即该旧值O就是目前来说最新的值了,自然而然可以将新值N赋值给V。反之,V和O不相同,表明该值已经被其他线程改过了则该旧值O不是最新版本的值了,所以不能将新值N赋给V,返回V即可。当多个线程使用CAS操作一个变量是,只有一个线程会成功,并成功更新,其余会失败。失败的线程会重新尝试,当然也可以选择挂起线程
在J.U.C包中利用CAS实现类有很多,可以说是支撑起整个concurrency包的实现,在Lock实现中会有CAS改变state变量,在atomic包中的实现类也几乎都是用CAS实现
CAS的问题:
ABA问题
因为CAS会检查旧值有没有变化,这里存在这样一个有意思的问题。比如一个旧值A变为了成B,然后再变成A,刚好在做CAS时检查发现旧值并没有变化依然为A,但是实际上的确发生了变化。解决方案可以沿袭数据库中常用的乐观锁方式,添加一个版本号可以解决。原来的变化路径A->B->A就变成了1A->2B->3C。java这么优秀的语言,当然在java 1.5后的atomic包中提供了AtomicStampedReference来解决ABA问题,解决思路就是这样的。
自旋时间过长
使用CAS时非阻塞同步,也就是说不会将线程挂起,会自旋(无非就是一个死循环)进行下一次尝试,如果这里自旋时间过长对性能是很大的消耗。如果JVM能支持处理器提供的pause指令,那么在效率上会有一定的提升。
只能保证一个共享变量的原子操作
当对一个共享变量执行操作时CAS能保证其原子性,如果对多个共享变量进行操作,CAS就不能保证其原子性。有一个解决方案是利用对象整合多个共享变量,即一个类中的成员变量就是这几个共享变量。然后将这个对象做CAS操作就可以保证其原子性。atomic中提供了AtomicReference来保证引用对象之间的原子性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。