java基础(完整版)
java
javaSE
注释:
block块级
line 行
标识符和关键字
组成部分、变量名、类名、方法名--------标识符
$\数字、_\字母
数据类型
强类型语言---安全性高--速度会慢
- 基础类型
- 引用类型
int-----128·127
整数、浮点数、字符类型、Boolean类型
类、接口、数组
位、字节、1B=8bit位 字符:是指计算机中使用的字母、数字、字、符号
八进制:前缀 0
十六进制: 0x
float f = 0.1f //有限的、离散的、、舍入误差
double f = 0.1
BigDecimal大数据类型 数据工具类
Unicode 编码
Excel 最长是2的16次方 ---65536
\u0061
转义字符
\t制表符 \n换行
类型转换
jadk7 新特性
数字之间 10_0000_0000
- 强制转换 (类型)变量名 高---低
- 自动转换 低---高
变量:
type varName [=value'][{,varNmae[=value]}]
作用域:
- 类变量:static 关键字修饰的 static salary =
- 实例变量:属于对象,如果不初始化,会输出默认值
- 局部变量:方法内:局部变量必须声明和初始化
常量:
String X="3.14"
修饰符:修饰符不存在先后顺序
final
运输符
- 算数运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 条件运算符:三目运算符?:
- 扩展运算符
自增运算++
自减运算符--
包机制
package
阿里巴巴开发手册
javaDoc生成手册
java的流程控制
Scanner对象 获取用户的输入
Scaner s = new Scanner (System.in);
next()和nextLine()方法获取输入的字符串
读取前用hasNext()和haseNextLine()是否有之前输入的数据
main(){
Scaner s = new Scanner (System.in);
if(s.hasNext()){
String ss =s.next();
syso("输出的内容是"+ss);
}
//关闭流
s.close();
}
next()输入的是遇到的空白会去除,不能代有空白的字符
进阶:hasNextInt();
顺序结构
选择:if
- 单选泽
- 双选择
- if的多选择
- if的嵌套选择
- switch的多选择
- case
- break
- default
循环结构
- while
- do....while
- for循环
jdk5 增强型的for循环
break、countinue
goto关键字
方法
方法头----方法体
- 修饰符
- 返回值
- 返回值类型
- 参数类型
- 形参
- 实参
- 方法体
方法的重载
- 方法名相同
- 参数不同
- 返回值可以不同
- 仅仅返回类型不同不能成为方法的重载
命令行执行的
javac编译
java运行要在src下面才能运行
可变参数
jdk1.5之前
- (double... i)这样就能输入多个 i
- 每一个方法中只能指定一个可变参数,它必须是最后一个参数
递归
A方法自己调用自己
- 递归头
- 递归体
if(n==1){
return 1;
}else{
return n*f(n-1);
}
数组
- 相同的数据类型的数据集合
- 按照一定的先后次序排列组合
- 通过下标来访问他们
声明---创建
- 建议 String[] a;
- String a [];//c 和c++才有不专业
String [] a =new int [10];
a[i]--取值
数组的默认初始化
- 数组是引用类型,他的元素相当于类的实例变量,因此数组一经过分配空间,其中的每个元素也按照实例变量同样的方式被隐藏初始化
- 静态初始化::栈
- 动态初始化::栈和堆
基本特点:
- 数组的长度是确定的
- 元素必须是相同的类型
- 数组的元素可以是任何的数据类型
- 数组变量属于引用类型
- 数组本身是对象,java的对象是在堆中的,数组对象是在堆中的
- 下标的合法区间[0,n-1]
- 数组也是对象,数组的元素属于成员变量
- 数组的长度是确定的,不可变,不能越界
数组的使用:
int[] a ={1,2,3,4,5};
//打印全部的数组元素
for(int i-0;i<a.length;i++){
syso.(a[i]);
}
//计算所有元素的和
int sun=0;
for(int i-0;i<a.length;i++){
//sum=sun+a[i]
sun+=a[i];
}
//查找最大值
int max=arry[0];
for(int i-1;i<a.length;i++){
if(array[i]>max){
max=array[i];
}
}
//增强的for循环 但是取不到下标
for(int array :a){
syso(a);
}
//反转数组
int[] result new int[a.length];
for(int i-1;j<result.length;i<a.length;i++;j--){
result[j]=a[i];
}
多维数组:
int[][] a={{1,2,3},{1,2,3},{1,2,3}};
遍历
for(int i=0;i<a.length;i++){
for(int j=0;j<a[i].length;j++){
syso.(a[i[j]]);
}
}
对象Arrays
java.util.arrays
int[] a ={1,2,3,4,5};
static修饰可以直接用
- 打印数组的元素
Arrays.toString(a);
- 排序
Arrays.sort(a);//升序
- 赋值
fill方法
Arrays.fill(a,0);
- 比较
比较equals方法 //比较数组的元素是否相等
- 查找
binarySearch能对排序好的查找进行二分查找
稀疏数组
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小的规模的数组中,从而缩小小程序的规模
int[][] array =new int[11][11];
array[1][5]=10;
array[5][5]=5;
//获得有效值的个数
int sum =0;
for(int i=0;i<array.length;i++){
for(int j=0;j<array[i].length;i++){
if(array[i][j]!=0){
sum++;
}
}
}
//创建稀疏数组
int [][]array2 =new int[sum+1][3];
array2[0][0]=11;
array2[0][1]=11;
array2[0][2]=sum;
//遍历二位数组,将非零的值存放在数组中
int count =0;
for(int i=0;i<array.length;i++){
fro(int j =0;j<array[i].length;j++){
if(array[i][j]!=0){
count++;
array2[count][0]=i;
array2[count][1]=j;
array2[count][2]=array[i][j];
}
}
}
八种排序算法
- 冒泡排序 O(n2)
for(int i=0;i<array.length-1;i++){
for(int j=0;j<array.length-i;j++){
if(array[j+1]>array[j]){
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
- 选择排序
for(int i =0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int tem = arr[i];
arr[i]=arr[j];
arr[j]=tem;
}
}
}
- 二分查找(前提是拍好序列)
内存分析
堆
- new的对象
- 数组
- 可以被所有的线程所共享的,不会存放别的对象的引用
栈
- 存放基本的变量类型
- 引用的对象的变量
方法区
- 可以被所有的线程共享
- 包含了所有的clss和static变量
面向对象编程
oop
对于描述复杂的事物,为了从宏观上把握,从整体上合理的分析,我们需要使用面向对象的思想来分析整个系统。但是在微观操作上,仍然需要面向过程的思想去处理
本质:已类的方式组织代码,已对象的组织(封装数据)
特征:
- 封装
- 继承
- 多态
方法的调用、值传递、类和对象的关系
类是一种抽象的数据类型,但是不能表示一种具体的事物
对象是抽象的概念的具体实例
new 创建对象
类:
- 属性
- 方法
抽象的类要实例化
构造方法:
- 必须和类的名字相同
- 必须没有返回值
new的内存分析:
。。。。
封装:
- 属性私有,get/set
高内聚,低耦合
继承
java类中只有单继承,没有多继承
extends
- 继承是类和类之间的关系,类之间的关系好友依赖,组合,聚合
- 继承是两个类的关系 源生类 派生类
- 子类和父类之间有is a关系
- Object
- super
- 方法的重写
Super方法
super.父类的方法直接写就行了
this当前类的
调用构造器只能在第一行
默认调用父类的构造器
如果父类没有无参的构造,子类也无法写无参的构造
子类的代码会隐藏父类的无参的构造
注意:
- super调用父类的构造方法,必须在构造方法的第一个
- super必需只能出现在子类的方法或者构造方法中
- super和this不能同时使用
前提:
- this本身调用者这个对象
- super:代表父类对象的应用
构造方法:
- this()本身的构造
- super父类的构造
重写@Override
- 非静态的
- 私有的不行
- 方法名必须相同
- 参数列表相同
- 修饰符可以扩大
- 重写可能会抛出异常,异常可以缩小不能扩大
public》protected》Default》private
多态
动态编译
interface implements
存在的条件:
- 有继承关系
- 子类重写父类的方法
- 父类的引用指向子类对象
父类的应用指向子类
B b =new A();
class A extends B;
注意事项:
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系 类型转换异常ClassCastException
- 存在条件。。。。。前面有
方法要被重写--多态:
不能重写的
- static属于类,不属于实例
- final 修饰的无法 在常量池内
- private修饰的
instanceof 判断一个数据的类型
x instanceof y;
x和y之间是否有父子关系
类型转换,子类的方法要类型转换。
子类----父类 ---向上转型
父类---子类------强制转换
Static
private static int a;//多线程
private int b;
静态变量在一个特别的区里面;
public static run(){
}
public go(){
}
- 静态方法只能静态方法
- 静态方法随着类加载生成
- 可以直接使用(静态代码块)
静态代码块static{}------匿名代码块{}-----构造方法
静态导入包
import static java.lang.PI;
被final定义的类不能被继承----断子绝孙
抽象类
abstract:
抽象类的子类要实现抽象类的所有的方法;
extends
但是是单继承
- 不能new抽象类,只能那子类去实现它:约束
- 抽象类里面可以放普通方法
- 抽象方法必须放在抽象类里面
接口
interface implements
- 只有规范,面向接口编程
接口的本质是契约
接口的方法都是public abstratact 的
- 类实现接口必学实现接口中的方法
- 接口里面的变量是常量public static final
接口不能实例化,没有构造方法
内部类
- 成员内部类
- 静态内部类
- 局部变量
- 匿名内部类
public Class outer{
private int id;
public void out(){
syso(“外部类”)
}
//内部类可以获得外部类的私有属性和方法
public class Inner{
public void in(){
syso("内部类");
}
}
}
//内部类
Outer out =new Outer();
Outer.Innter inner= out.new Inner();
静态内部类:
public Class outer{
private int id;
public void out(){
syso(“外部类”)
}
//内部类可以获得外部类的私有属性和方法
public static class Inner{
public void in(){
syso("内部类");
//这里就拿不到外部类的变量的值了,静态内部类无法访问静态方法
}
}
}
局部内部类:
在方法里面写内部类:
public Class outer{
private int id;
public void out(){
syso(“外部类”)
public class Inner{
public void in(){
syso("内部类");
}
}
}
}
一个java里面可以写多个class类
class a{}
class b{}
匿名内部类:
接口 a = new 接口(){
@Override
方法
}
Error和Exception
- 异常是指运行中出现不预期的各种状况
- 异常发生在程序运行期间,他影响了正常的执行流程
异常分为:
- 检查性异常
- 运行时异常
- 错误Error
java.lang.Throwable 超类
- ArrayLndexOutOfBoundsException 数组线标越界
- NullPointerExceotion空指针异常
- ArithmeticException算数异常
- MissingResourceException 丢失资源
- ClassNotFountException找不到类等异常
抛出异常:
捕获异常
关键字:
try catch finally throw throws
throw new ArithmeticException();//抛出一个异常
//一般在方法中使用,加入这个方法处理不了这个异常,就抛出异常
public void a throws ....
自定义异常
public ex extends Exception{
//传递数字》10
private int detail;
public ex (int a){
this.detail = a;
}
@Override
public String toString(){
return "myex{"+detail+"}";
}
}
throw new ex();
javaEE
常用API
Object类
public String toString() //打印地址 :类的全类名@内存地址
- 存在的意义:为了被子类去重写,以便于返回对象的内容信息,而不是地址信息
public Boolean equals()//默认比较两个对象的地址是否相同
public Boolean equals(Object o){
// 1.不是同一个对象比较
if(this==0) return true;
//o是不是null
if(o==null||this.getClass()!=o.getClass()) return false;
//o一定是Student类型不是null
Student student =(Student) o;
return sex==student.sex&&age==student.age&&Object.equals(name,student.name);
}
StringBuilder
可变的字符串类,看成一个对象容器
提高效率,如凭借、修改
1.构造器:
- public StringBuilder()
- public StringBuilder(String str)
2.常用方法
- public StringBuilder append(任意类型) 添加数据并返回StringBuilder 对象本身
- public StringBuilder reverse() 将对象的内容反转
- public in length() 返回对象的长度
- public String toString() 通过toString() 就可以实现StringBuilder 转换为String
//例子
public static String toString(int[] arr){
if(arr!=null){
StringBuilder sb =new StringBuilder ("[");
for(int i =0;i<arr.length;i++){
sb.append(arr[i]).append(i==arr.length-1?"":",");
}
//这里不能少;恒优雅
sb.append("]");
return sb.toString();
}else{
return null;
}
}
Math
没有提供公开的构造器、工具类
全部是静态方法
- public static int abs(int a) 获得绝对值
- public static double ceil (double a) 向上取整
- public static double floor(double a) 向下取整
- public static int round(float a) 四舍五入
- public static int max(int a,int b) 获得两个int 的较大值
- public static double pow(double a,double b) 返回a 的幂
- public static double random() 返回double的随机数【0.0,1.0)
System
System类的功能是相同的,都是直接用类名调用即可
System的方法:
- public static void exit(int status) 终止当前的java虚拟机,非0表示异常的终止
- public static long currentTimeMillis() 返回当前系统的事件毫秒值形式
- public static void arraycopy (数据源数组,启始索引,目的地数组、起始索引、拷贝个数) 数组拷贝
BigDecima
解决浮点型的精度失真
public static BigDecima valueOf(double val); 包转成浮点对象为BigDecima对象
- public BigDecima add(BigDecima b)加法
- public BigDecima subtract(BigDecima b)减法
- public BigDecima multiply (BigDecima b)乘法
- public BigDecima divide (BigDecima b)除法
- public BigDecima divide(舍入模式)除法
日期和事件
Date类在当前系统所在的此刻的日期时间
java.util.Date
Date time =new Date();
- public long getTime() 获取对象的毫秒值1970.1.1.00.00
long time =System.currentTimeMillis();
- 毫秒值转变成日期对象
public Date(long time);
public void setTime(Long time)
设置日期对象的事件为当前的时间毫秒值的时间
SimpleDateFormat构造器
- public SimpleDateFormat() 默认格式
- public **SimpleDateFormat(String pattern)指定格式
SimpleDateFormat的格式化方法
- public final String format(Date date) 将日期格式化成日期/时间字符串
- public final String format (Object time)将时间毫秒值转化为日期/时间字符串
- public Date parse(String source) 从给定字符串的开始解析文本以生成日期
y 年
M 月
d 日
H 时
m 分
s 秒
yyyy-MM-dd HH:mm:ss
Date d =new Date();
SimpleDateFormat sdf =new SimpleDateFormat(yyyy-MM-dd);
String s =sdf.format(d);
String s ="2022年05月06日 14:55:60"
SimpleDateFormat sdf =new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date d = sdf.parse(s);
long time =d.getTime()+(2L*24*60*60+14*60*60+49*60+6)*1000;
sdf.format(time);
Calendar
- Calendar代表系统此刻日期对应的日期对象
- Calendar是抽象类,不能直接创建对象
Calendar cal =Calendar.getInstance();
//先获取字段 syso (cal)
int year =cal.get(Calendar.YEAR);
int moth =cal.get(Calendar.MONTH);
int days=cal.get(Calendar.DAY_OF_TEAR);
cal.set(Calendar.HOUR,12);//一般不会修改日历
//64天后的日期
cal.add(Calendar.DAY_OF_TEAR,64);
JDK8新增的日期类
- LocalDate:不包含具体时间 2022-05-06getYear()\ getMonth()\getDayofMonth、getDayofYear、getDayofWeek、getMonth().getValue()、getMonthValue()
- LocalTime:不包含日期时间
- LocalDateTime:包含了日期时间
- Instant代表的是时间戳得到的是标准时间英国
- DateTimeFormatter用于做时间的格式化和解析
- Duration用于计算时间的间隔
- Period用于计算两个日期的间隔
和String类似
- public static Xxxx now();静态方法,当前时间创建对象
- public static Xxxx of(.....);静态方法,指定时间创建对象
包装类
8种数据类型的引用类型
byte-----Byte
short ------Short
int ------Integer
char----Character
float----------Float
double-------Double
boolean---Boolean
long------Long
- 为了实现一切皆对象
- 后面的集合和泛型只能支持包装类型,不支持基本数据类型
自动拆箱:包装类的变量可以直接赋值给基本数据类型
自动装箱:基本类型的数据和变量可以直接赋值给包装类的变量
特有功能:
- 包装类的变量的默认值可以是null 容错率更高
- 基本数据类型变成字符串
- 调用toString()方法
- Interger.toString(基本数据类型的数据)
- 可以把字符串的数值变成真实的数据类型
- Interger.parseInt("字符串类型的整数");
正则表达式
public static boolean checkQQ(String s){
if(s==null||s.length()<6||s.length()>20){
return false;
}
//判断是否全为数字
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
if(ch<'0'||ch<'9'){
return false;
}
}
return true;
}
//简化正则
public static checkQQ(string s){
return s!=null&&s.matches("\\d{6,20}");
}
- 字符类
- [abc]
- [a-zA-z]
- [a-z&&[^ m-p]]
默认匹配一个字符
预定义的字符集:
- . 任何字符
- \d 一个数字:[0-9]
- \D 非数字
- \s 一个空白的字符
- \S 非空的字符
- \w 英文数字下划线
- \W 一个非单词字符
匹配多个字符
- X? 一次或根本不
- X* 零次或多次
- X{n} 正好n次
- X{n, }至少n次
- X{n,m} 至少n次但不超过m次
public boolean matches (String regex);
//例子
String ss ="........"
//1.写正则:
String regex =""
//2.把爬取的规则编译成对象
Pattern pa =Pattern.compile(regex);
//3.得到一个内存匹配的对象
Matcher ma = pa.matcher(ss);
//开始查找
whil(matcher.find()){
String rs = matcher.group();
}
Arrays
数组的工具类:
- public static String toString(类型[] a) 对数组进行排序
- public static void sort (类型[] a) 对数组进行默认升序排序
- public static void sort(类型[] a,Comparator<?superT>c) 使用比较器对象自定义排序
- public static int binartSearch(int [] a,int key) 二分搜索数组中的数据,存在返回索引,不存在返回-1**-(插入的位置的索引+1)
int[] a={1,2,3,5,6,10};
Arrays.方法(a);
//比较器
Integer [] ages={1,2,3,4,5,6};
Arrays.sort(ages,new Comparayor<Integer>(){
@Override
public int compare(Integer o1,Integer o2){
//指定比较规则
return -(o1-o2);
}
})
- 认为左边数据 大于 右边数据 返回正数
- 认为左边数据 小于右边数据 返回负数
- 认为左边数据 等于 右边数据 返回 0
Lambada表达式
只能简化函数接口的匿名内部类,一个抽象方法,
- @FunctionalInterface加注解
懒汉
- 参数类型可以省略不写
- 如果只有一个参数,参数类型可以省略,同时()也就可以省略
- 如果lambda表达式的方法代码只有一行代码,可以省略大括号,同时省略分号
- 如果lambda的方法体的代码只有一行代码,如果还是return语句,必须省略return,也不写;
作用简化匿名内部类的简化代码
(匿名内部类的重写的形参列表)->{
被重写的方法
}
//简化匿名内部类的代码
@FunctionalInterface
public interface aaa{
public void run();
}
main(String[] args){
aaa a =()->{
syso("Lambada的方法");
}
}
go(()->{
syso("Lambada的方法");
})
Collection集合体系
集合和数组都是容器。
- 数组定义完后,类型确定,长度固定。
- 数组增删都要放弃原有的数组或者移位
集合:
- 大小不确定
- 存储对象的一种容器
- 非常适合做增删改查
- 基本的数据类型用包装类
Collection单列:每个元素只包含一个值
Map双列:j键值对
Collection是一个接口:
- List
- ArrayList
- LinkedList
- Set
- HashSet
- LinkedHashSet
- TreeSet
- HashSet
Collection集合特点:
- List集合:添加的元素是有序、可重复、有索引
- ArrayList、LinkedList:有序、可重复、有索引
- Set集合特点:添加的元素是无序、不重复、无索引
- HashSet:无序、不重复、无索引;LinkedHashSet:有序、不重复、无索引
- TreeSet:按照大小默认升序,不重复,无索引
Collection<String> c =new ArratList<String>();//约束数据类型
Collection<String> c =new ArratList<>();//JDK1.7后可以不写
add() 添加
集合常用API
- 添加元素add()
- 清空集合clear()
- 判断集合是否为空isEmpty()
- 获取集合的大小size()
- 判断集合中的元素是否存在contains()
- 删除集合中的元素remove() 默认只会删除前面的一个,要删除所有的要边遍历,边判断删除
- 把集合变成数组Object[] a = array.toArray();
集合的遍历方式
1.迭代器:
- 遍历就是一个一个的把容器中的元素访问一遍
- 迭代器在java中的代表是Iterator,迭代器是集合的专用的遍历方式
获取结合的迭代器:
Collection<String> sc =new ArrayList<>();
sc.add("我");
sc.add("是");
sc.add("大");
sc.add("佬");
sc.add("!");
//1.获得迭代器
Iterator<String> it =sc.iterator();
while(it.hasNext()){
String a=it.next();
}
用一次,取一次越界会有越界异常
2.forEach遍历
- 既可以遍历集合也可以遍历数组内部是迭代器,但是迭代器不能遍历数组
- 实现了Iterator接口才能使用
格式:
for(元素数据类型 变量名:数组或者Collection集合){
在此用变量即可,该变量就是元素
}
Collection<String> list =new ArrayList<>();
....
for(String it:list){
syso(it);
}
3.lambda表达式
- default void forEach(Consumer<? super T> action) 结合lambda遍历集合
list.forEach(new Cousumer<String>(){
@Overrride
public void accept(String s){
System.out.print(s);
}
})
数据结构
栈:
后进先出、先进后出
- 进入栈:压/进栈
- 出栈:弹
队列
先进先出,后进后出
- 入队列
- 出队列
数组
- 查询速度快,查询数据通过地址值和索引定位,查询任意诗句的耗时相同。(元素中在内存中是连续存储的)
- 删除效率低:要将原始数据删除,同时后面的每个数据前移
- 添加效率极低:添加位置后的每个数据后移,再添加元素
链表
链表中的元素是游离存储的,每个元素的节点包含数据值和下一个元素的地址
- 链表的查询慢,无论查哪个数据都是从头开始
- 链表的增删较快
单向链表
双向链表(java多用)
二叉树
父节点:左右节点、值
- 只能有一个根节点
- 节点的度
- 高度
- 层
- 兄弟节点
二叉查找树
目的:提高检索数据的性能
会变成瘸子
平衡二叉树
任意节点的左右两个自树的高度不超过1,任意节点的左右的两个自树都是一颗平衡二叉树
- 进行左旋或者右旋
情况:左边高右边拉,右边高左边拉
红黑树
平衡二叉B树
- 每个节点是红色或者是黑色,根节点是黑色
- 如果一个节点没有子节点或者父节点,则该节点相应的指针值为Nil,这些Nil视为叶节点,叶节点是黑色的
- 如果一个节点是红色的,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
- 对于每个节点,从节点到所有的其所有的后代叶子节点的简单路径上,均包含相同的黑色节点
添加节点:
- 添加节点可以黑色可以红色
- 默认红色效率高
牛逼红黑树
List集合
- ArrayList、LinedList:有序、可重复、有索引
- 有序:存储和取出的元素顺序是一致的
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复操作
特有方法:
- void add(int index,E element) 在集合中的指定位置插入指定的元素
- E remove(int index) 删除指定索引出的元素,返回被删除的元素
- E set (int index, E element)修改指定索引处的元素,返回被修改的元素
- E get ( int index) 返回指定索引处的元素
//多态
List<String> arr =new ArrayList<>();
arr.add("Java");
arr.add("Mybatis");
arr.add("Mysql");
//特有
arr.add(2,"HTML");
arr.remove(2);
arr.get(2);
arr.set(1,"springboot");
遍历方式:
通用的遍历方式都可以
ArrayList的底层原理:
基于数组实现
- 在底层先创建一个10的默认数组
- 有一个size指针指向下个位置--------现在的数组的长度
- 扩容1.5倍,元素重新迁移
LinkedList的底层原理:
基于双链表,查询慢,首尾操作快,但是查询很慢
特有方法:
- public void addFirst(E e) 在该列表的开头插入指定的元素push()
- public void addLast(E e)将指定的元素追加到此列表的末尾pop()
- public E getFirst()返回此列表中的第一个元素
- public E getLast()返回此列表的最后一个元素
- public E removeFirst()从此列表中删除并返回第一个元素
- public E removeLast()从此列表中删除并返回最后一个元素
//栈
LinkedList<String> stack =new LinkedList<>();
stack.addFirst("java");
stack.addFirst("HTML");
stack.addFirst("CSS");
stack.addFirst("Spring boot");
//删除并取出
stack.removeFirst()
//队列
LinkedList<String> query =new LinkedList<>();
query.addLast("java");
query.addLast("HTML");
query.addLast("CSS");
query.addLast("Mybatis");
//删除并取出
query.removeFirst()
集合的并发修改异常
1.使用迭代器删除当前位置的元素不后移
Iterator<String> arr =list.iterator();
while(arr.hasNext()){
String ss= is.next();
//list.remove(); 这是不对的,要用迭代器来用remove
it.remove();
}
2.foreach和lambda也是有bug的
3.for会漏删
结局方案:倒着删
泛型
- 统一数据类型
- 把运行时间出现的错误,编译时就能出现
自定义泛型类(架构师技术)
- 定义了类的同时定义了泛型的类就是泛型
- 泛型的格式
修饰符class类名<泛型变量>{}
public class MyArrayList<T>{
public void add(T t){
}
}
MyArrayList<String> a =new MyArrayList<>();
a.add("字符串");
自定义泛型方法
格式:
public<T> void show(T t){}
接受任意类型的参数
public<T> void show(T[] t){
StringBuilder sb =new StringBuilder("[");
if(t==null){
return false;
}
for(int i =0;i<t.length;i++){
sb.append(t[i]).append(i==t.length-1?"":",");
}
sb.append("]");
System.out.print(sb);
}
泛型接口
public interface Data<E>{}
public interface Data<E>{
public void add(E e);
}
泛型的通配符
- ?可以在使用泛型时代表一切类型
- E T K V是在定义泛型时使用
go(ArrayList<?> a){}
- ?extends Car:必须是Car或者其子类 泛型上线
- ?super Car:必须是Car或者是器父类 泛型下限
Set集合
- HashSet
- LinkedHashSet
- TreeSet
特点:
- 无序行:存取的顺序不一致
- 不重复:可以去重复
- 无索引:没有带所有的方法
HashSet:无序、不重复、无索引
LinkedHashSet:有序、不重复、无索引
TreeSet:排序、不重复、无索引
//多态
Set<String> s =new HashSet<>();
//使用Collection的API
s.add("JAVA");
s.add("HTML");
HashSet:
采取的是哈希表组成
- JDK8以前是数组和链表
- JDK8以后数组+链表+红黑树
哈希值
- JDK根据对象的地址,按照某种规则算出来的int类型的数值
String name ="我";
name.hashCode();--------得到哈希值
JDK1.7
- 先创建一个16长度的数组 ---table
- 根据元素的哈希值和数组的长度求余数,计算出应存入的位置(哈希算法)
- 当前位置为null直接存入
- 如果不为null用equals方法比较
- 一样则不存,不一样则存入数组
- JDK7中新元素占用老元素的位置,指向老元素
- JD8中新元素挂在老元素下面,当长度超过8变为红黑树
- 每次扩容为原来的2倍
先算hashCode哈希值然后在equals比较。
LinkedHashSet:
底层的机制依然是哈希表,但是每个元素又多了一个双链表的机制记录存储的顺序
- 有序 不重复 无索引
TreeSet
- 不重复 无索引 可排序
Tree集合基于红黑树的数据结构实现排序
按照大小排序,string 按照首字符的编号排序
方法一:
//实现Comparable接口
重写里面的compareTo方法实现排序
方法二:
Tree集合有参的构造器,实现对应的Comparable的对应的比较器,来制定比较规则
区分方法
希望元素可以重复,又有索引,索引查找快
- ArrayList
希望元素可以重复,又有索引,增删首尾
- LinkedList
如果希望增删改查都很快,但是元素没重复,无序、无索引
- HashSet
可变参数
可变参数的格式
数据类型...参数名称
main(String[] args){
run();
run(1,2,3,4);
run(new int[]{10,20,30})
}
public static void run (int... nums){
//外面看见的是一个参数,实际上是数组
Arrays.toString(nums);
}
- 一个形参列表中只能有一个可变参数
- 可变参数只能在最后
Collections集合工具类
java.util.Collections:是集合的工具类
作用:Collections并不属于集合,是用来操作集合的工具类
常用的api
- public static boolean addAll(Collection<? super T> c,T... elements) 给集合对象添加元素
- public static void shuffle(List <?> list) 打乱集合元素的顺序
排序:
- public static static void sort(List list) 将集合中元素按照默认规则排序
- public static void sort (List list,Comparator<? superT >c) 将集合中的元素按照指定的规则排序
List<Integer> list =new ArrayList<>();
Collection.addAll(list,23,5,6,4);
Collection.sort(list);
- 实现ComparaTo接口 实现compare方法
Map集合体系
双列集合体系
key=value(键值对集合)
- Collection集合格式:[元素1,元素2,元素3]
- Map集合格式:
Map<K,V>
- HashMap
- LinkedHashMap
- TreeMap
- HashTable
- Properties
特点:
- Map集合的特点都是由键决定的
- Map的集合的键是无序的,不重复的,无索引的,值可以重复
- Map集合后面重复的键对应的值会覆盖前面重复的值
实现类的特点:
- HashMap:元素按照键是无序的、不重复的、无索引的、值不做要求。(与Map体系一样)
- LinkedHashMap:元素按照键是有序的,不重复的,无索引的,值不做要求
- TreeMap 元素是按照键是排序的,不重复,无索引的,值不做要求
Map的API
- V put (key , V value) 添加元素
- V remove (Object key) 根据键值对删除对应的元素
- void clear() 移除所有的键值对
- Boolean containsKey(Object key)判断集合是否包含指定的键
- Boolean containsValue(Object value) 判断集合是否包含对应指定的值
- Boolean isEmpty() 判断集合是否为空
- int size() 集合的长度也就是集合中的键值对的个数
- V get(Object key) 根据键得到对应的值
- Set keySet() 获得全部的键的集合
- Collection values(); 获得全部值的集合
- putAll(Map a)合并其他的Map集合
遍历Map集合
1.键找值
...
Map<string,Integer> s = new HashMap<>();
....
Set<String> keys =s.keySet();
for(String key:keys){
int value=s.get(key);
syso(value);
}
2.键值对流程
先把Map集合转化成Set集合,Set集合中的每个元素都是键值对的实体类型了
遍历Set集合,然后提取键以及提取值
- Set<Map.Entry<K,V> >entrySet() 获得所有的键值对
- K getKey() 获得键
- K getValue() 获得值
...
Map<string,Integer> s = new HashMap<>();
....
//把Map集合变成Set集合
Set<Map.Entry<String,Integer>> entries = s.entrySet();
for(Map.Entry<String,Integer> entry :entries){
String key =entry.getKey();
String value =entry.getValue;
}
3.lambda
- default void forEach(BiConsumer<? super K, ? super V> action) 结合lamdba 遍历
maps.forEach( new BiConsumer<String,Integer>(){
@Overrride
public void accept(String key,Integer value){
syso(key------value)
}
})
HashnMap
- 无序、不重复、无索引
- 底层:哈希表 依赖hashCode和equals来判断键唯一
Set系列集合的底层就是Map实现的,只是Set集合中的元素只要数据,不要键而已
LinkedHashMap
- 有序、不重复、无索引
- 底层是:哈希表和双链表
TreeMap
- 不重复、无索引、可排序
- TreeMap的底层和TreeSet的底层原理是一样的
自定义排序:
- 类实现Comparable接口
- 自定义Comparator比较器对象,重写比较规则
集合嵌套
统计投票人数:
Stream流
简化集合和数组操作的API
List<String> list =new ArrayList<>();
Collection.addAll(list,"张1","张2","王");
List<String> list2 -new ArrayList<>();
for(String name:list){
if(name.startsWith("张“)){
list.add(name);
}
}
list.Stream().filter(s->s.startsWith("张").filter(s->slength()==2)).forEach(s->System.out.print(s));
技术思想:
- 先得到一个集合或者数组的Stream流
- 把元素放到传送带上
- 然后就使用Stream流简化API来操作元素
Stream的获取
- 集合获得Stream流的方式
- 可以用Collection接口中的默认方法Stream()
default Stream stream() 获得当前集合对象的Stream
- 数组获得Stream流的方式
- public static Stream stream (T[] array) 获取当前数组的Stream流
- public static Stream of(T...values) 获得当前的数组/可变数据的Stream流
日志技术
- 将系统执行的信息选择性的输出到指定的位置
- 多线程
- 日志规范
- 日志框架(JCL slf4j)
- log4j 性能不好
- JUI
- Logback加强
logback日志框架
- logback-core:为其他的模块提供了基础,必须有
- logback-classic 他是log4j的一个改良版本,同时它实现了slf4j的api
- logback-access模块和tomcat和jetty等servlet容器集成,已提供http访问日志功能
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="F:\\logback.log" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="com.example.logback.filter.MyFilter" /> -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<logger name="com.example.logback" level="warn" />
</configuration>
创建logback的日志对象
public static Looger LOGGER=LoggerFactory.getLogger("Test.class");
LOGGER.debug("main方法开始");
LOGGER.info("我记录的日志第二行");
LOGGER.trace("a="+a);
LOGGER.error("有错误");
配置
输出到控制台:
..
日志级别
trace<debug<info<warn<error
File、方法递归、IO流
File类可以定位文件:进行删除、获取文件本身的信息
- 但是不能读写文件内容
IO流可以对硬件中的文件读写
字节流--音乐视频文件
字符流---文本文件
File类
java.io.File 代表操作系统的文件对象(文件、文件夹)
- 定位文件
- 获取文件本身的信息
- 删除文件
- 创建文件
常用方法:
- public File(String pathname) 根据路径创建文件对象
- public File (String parent, String Child) 从父路径名字字符和子路径名字字符创建文件对象
- public File (File parent , String Child) 根据父路径对应文件对象和子路径名字字符串创建文件对象
main(){
//创建File对象
//路径写法:D:\\hjz\\a.jpg
// D:/hjz/a.jpg
//File.separator 分隔符
//相对路径 相对到工程下 "sprngboot/src/data.txt"
File f = new File("D:\\hjz\\a.jpg");
Long a =f.length();//文件的字节大小
f.exists()//判断文件夹是否存在
}
方法:
- public boolean isDirectory () 测试 抽象路径名表示的File是否为文件夹
- public boolean isFile()测试 抽象路径名表示的File是否为文件
- public boolean exists()测试 抽象路径名表示的File是否存在
- public String getAbsolutePath()返回的抽象路径名的绝对路径名字符串绝对路径
- public String getPath()将此抽象路径名转化为路径名字符串自己定义的路径
- public String getName()返回由此抽象名表的文件或文件夹的名称
- public Long lastModified()返回文件最后修改的时间的毫秒
File f = new File("D:\hjz\a.jpg");
方法:
- 创建:
- public boolean createNameFile() 创建一个新的空白文件夹
- public boolean mkdir()创建一个一级文件夹
- public boolean mkdirs()创建一个多级文件夹
- 删除:
- public boolean delete() 删除由此路径表示的文件、空文件夹
删除非空文件夹要写算法
遍历:
- public String[] list() 获取当前目录下所有的一级文件夹名称“到一个字符串数组中去返回
- public File[] listFiles()常用 获取当前目录下所有的“一级文件对象”到一个文件对象数组中去返回重点
- 不存在返回null
- 调用者是文件返回null
- 调用者是一个空文件夹返回一个长度为0的数组
- 调用者是一个有内容的文件夹时,将里面的所有文件和文件夹的路径放在File数组中返回
- 包含隐藏内容
File f =new File("D://")
File[] names=f.listFiles();
for(File url:names){
syso(url.getAbsolutePath);
}
方法递归
1.递归公式
2.递归的终结点
3.递归的方向
1-n的阶乘
public static int f(int n){
if(n==1){
return 1;
}else{
return f(n-1)*n;
}
}
1-n的和
public static int f(int n){
if(n==1){
return 1;
}else{
return f(n-1)+n;
}
}
猴子吃桃子
/**
f(x)-f(x)/2-1=f(x+1)
等价变化:
f(x)=2f(x+1)+2
终点
f(10)=1
*/
public static int f(int n){
if(n==10){
return 1;
}else{
return 2*f(n+1)+2;
}
}
文件搜索
1.先定位一级文件对象
2.遍历一遍一级文件对象,判断是否文件
3.如果是文件,判断是否是自己想要的
4.如果是文件夹,需要继续递归进去重复上面的过程
public static void searchFile(File dir,String name){
if(dir!=null&&dir.isDirectory){
//可以找了
//获得一级文件对象
File[] files=dir.ListFiles();
//判断一级文件夹的对象
if(files!=null&&files.length>0){
for(File f:files){
//判断当前遍历的一级文件对象是文件还是目录
if(file.isFile()){
//是不是要找的文件
if(file.getName().contains(name)){
System.out.print("找到了文件是"+file.getAbsolutePath());
//启动它
Runtime r =Runtime.getRuntime();
r.exe(file.getAbsolutePath());
}else{
//是文件夹继续递归调用
searchFile(file,fileName);
}
}
}
}
}else{
System.out.print("当前搜索的不是文件夹");
}
}
啤酒问题
盖子换酒,瓶子换酒(套娃)
//买的一共酒
public static int totalNumber;
//记录瓶子的数量
public static int lastBottleNumber;
//记录剩余的盖子数目
public static int lastCoverNumber;
public static void buy(int money){
int buyNumber = money/2;
totalNumber+=buyNumber;
//换成钱
int coverNumber = lastCoverNumber+ buyNumber;
int bottleNumber=lastBottleNumber+buyNumber;
//统计一共多少钱
int allNumber=0;
if(coverNumber>=4){
allNumber+=coverNumber/4*2;
}
lastCoverNumber=coverNumber%4;
if(lastBottleNumber>=2){
allNumber+=(bottleNumber/2)*2
}
lastBottleNumber = bottleNumber%2;
if(allNumber>=2){
buy(allNumber);
}
}
IO流
字符集
- 计算机底层不能存储字符,只能存储0 1
- 二进制转化成十进制
ASCII字符集
- 美国信息交换标准代码:128个字符信息一个字节存储1个字符、一个字节是8位256
GBK字符集
- 包含了几万个汉字繁体字和日韩文字、兼容ASCII字符编码表一个中文用两个字节
Unicode码表
- 统一码是计算机科学领域栗的一项业界字符编码UTF-8(中文是以3个字节)\UTF-16
编码前的字符集和编码好的字符集要相同,不然会应为不同的字符位数错误
String编码
- byte[] getBytes()使用平台的默认字符集将String编码为一系列字节,将结果存储到新的字节数组中
- byte[] getBytes(String charsetName)使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
String解码
- String(byte[] bytes)通过平台的默认字符集解码在指定的字节数组构造新的String
- String(byte[] bytes,String charsetName)通过指定的字符集解码指定的字节数组来构造新的String
String s ="abc我爱你中国";
byte[] bytes = s.getBytes();//默认编码
byte[] bytes = s.getBytes("GBK");
String ss=new String(bytes);//默认编码
String ss=new String(bytes,"GBK");
IO流:输入输出流读写数据
- 字节流音乐,视频
- 字符流文本内容
读入到内存输入流
写入到磁盘输出流
字节流:
- InputStream
- FileInputStream
- public int read() 每次读一个,没有返回-1
- public int read(byte[] buffer)每次读一个字节数组,没有返回-1
- FileInputStream
- OutputStream
- FileOutPutStream
- public int write()
- public int write(byte[] buffers)
- public int write(byte[] buffers,int pos,int len) 写一部分
- FileOutPutStream
字符流:
- Reader
- FileReader
- Writer
- FileWriter
字节流
FileInputStream
- 上面的都是抽象类
- 实现类*水流模型
//1.创建一个输入流管道
//InputStream is =new FileInputStream(new File("D://..."));
//简化
InputStream is =new FileInputStream("D://...");
int b =is.read();//读取一个字节,读完返回-1
Byte[] buffer = new byte[1024];//1kb
InputStream is =new FileInputStream("D://...");
int len =is.read(buffer);
String rs= new String(butter);
常用方法:
Byte[] buffer = new byte[1024];//1kb
InputStream is =new FileInputStream("D://...");
int len=0;
while((len=is.read(butter)!=-1)){
System.out.print(new String(butter,0,len));
}
中文乱码无法解决
//自己暴力实现
Byte[] buffer = new byte[(int)file.length()];//1kb
File file =new File("D://...");
InputStream is =new FileInputStream(file);
int len=0;
while((len=is.read(butter)!=-1)){
System.out.print(new String(butter,0,len));
}
Byte[] buffer = is.readAllBytes();//JDK9出现
File file =new File("D://...");
InputStream is =new FileInputStream(file);
int len=0;
while((len=is.read(butter)!=-1)){
System.out.print(new String(butter,0,len));
}
FileOutPutStream
//写会自动生成
OutputStream os =new FileOutPutStream("D://a.txt");
//OutputStream os =new FileOutPutStream("D://a.txt",true); 追加数据
os.write('a');
os.writ(98);
//写数据必须刷新数据
os.flush();
os.close();//关闭资源
byte[] buffer ={'a',97,98,90};
os.write(buffer);
byte[] buffer ="我是中国人".getBytes("GBK");
os.writer(buffer);
//注意是没有换行的
os.write("\r\n".getBytes());//换行
文件拷贝
学会字节流完成文件的复制--支持一切
try{
//创建一个字节输入流
InputStream is =new FileInputStream("D://img.jpg");
//创建一个字节输出流和目标文件接通
OutputStream os =new FileOutPutStream("D://a.txt");
//定义一个字节数组
byte[] buffer = new byte[1024];
int len ;记录读取数据的字节数
while(len=is.read(buffer)!=-1){
os.write(buffer,0,len);
}
//关闭流
os.close();
is.close();
}catch (Exception ex){
e.printStackTrace();
}
资源释放的方式
InputStream is=null;
OutputStream os=null;
try{
//创建一个字节输入流
is =new FileInputStream("D://img.jpg");
//创建一个字节输出流和目标文件接通
os =new FileOutPutStream("D://a.txt");
//定义一个字节数组
byte[] buffer = new byte[1024];
int len ;记录读取数据的字节数
while(len=is.read(buffer)!=-1){
os.write(buffer,0,len);
}
}catch (Exception ex){
e.printStackTrace();
}finally{
//关闭流
//下面都要try ---catch---try{..}catch{...}
//非空校验 if(os!=null){os.close()}
os.close();
is.close();
}
JDK7:
try(
//创建一个字节输入流
InputStream is =new FileInputStream("D://img.jpg");
//创建一个字节输出流和目标文件接通
OutputStream os =new FileOutPutStream("D://a.txt");
){
//定义一个字节数组
byte[] buffer = new byte[1024];
int len ;记录读取数据的字节数
while(len=is.read(buffer)!=-1){
os.write(buffer,0,len);
}
//关闭流
os.close();
is.close();
}catch (Exception ex){
e.printStackTrace();
}
JDK9:
不好
字符流
- Reader
- FileReader
- Writer
- FileWriter
Reader fr=new FileReader("D://img.txt");
Writer fw=new FileWriter("D://a.txt");
int code = fr.read();//每次读取一个字符 没有返回-1
FileReader
- public FileReader(File file)
- public FileReader(String pathname)
常用方法:
- public int read()每次读取一个字符 没有返回-1
- public int read(char[] buffer)每次读取一个字符数组 没有返回-1
问题:
- 中文不会乱码
- 性能较慢
Filewrite
- public FileWriter(File file)
- public FileWriter(File file,boolean append)
- public FileWriter(String pathname)
- public FileWriter(String pathname,boolean append)
常用方法:
- public void write(int c)
- public void write(char[] cbuf)
- public void write(char[] cbuf,int off,int len)写一部分
- public void write(String str)
- public void write((String str,int off,int len)
- flush()
- close()
Write fw =new FileWrite("D://img.jpg");
fw.write('s');
缓冲流
自带缓冲区,提高原始流的性能
- BufferedInputStream
- BufferedOutputStream
- BufferedReader
- BufferedWriter
自带了8kb的缓冲池
字节缓冲流
构造器:
- public BufferedInputStream (InputStream is)
- public BufferedOutputStream(OutputStream os)
try(
//创建一个字节输入流
InputStream is =new FileInputStream("D://img.jpg");
BufferedInputStream bis =new BufferedInputStream(is);
//创建一个字节输出流和目标文件接通
OutputStream os =new FileOutPutStream("D://a.txt");
BufferedOutputStream ois=new BufferedOutputStream(os);
){
//定义一个字节数组
byte[] buffer = new byte[1024];
int len ;记录读取数据的字节数
while(len=bis.read(buffer)!=-1){
bos.write(buffer,0,len);
}
}catch (Exception ex){
e.printStackTrace();
}
字符缓冲流:
- public BufferedReader (Read is)
- public BufferedWriter(Writer os)
新增输入流方法:
- public String readLine() 读取一行数据,如果没有完毕,无行课读取返回null
输出流:
- public void newLine() 换行操作
转换流
不同编码读取会乱码
- 使用字符输入转换流
- 先提取原始的字节流
- 然后在将字节流转换成对应的编码格式
转换流:
- InputStreamReader
- OutputStreamWriter
构造器:
- public InputStreamReader (InputStream is)
- public InputStreamReader(InputStream is,String charset)
字符输出转换流:
- public OutputStreamWriter (OutputStream os)
- public OutputStreamWriter(OutputStream os,String charset)
序列化对象
对象字节输出流
ObjectOutputStream
对象字节输入流
ObjectInputStream
构造器:
- public ObjectOutputStream (OutputStream os)
- public ObjectInputStream (InputStream is)
//对象系列化
//实体类要实现接口
Serialiazble
Student st =new Student (...);
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream("D://a.txt"));
//调用序列化方法
oos.writeObject(st);
oos.close();
//对象反序列化
ObjectInputStream ois = new ObjectInputStream(new FileOutputStream("D://a.txt"));
//调用序列化方法
Student st=( Student)ois.readObject(st);
ois.close();
不想序列化
//实体类属性字段加
private transient String password;
打印流
字节输出流;
PrintStream
字符输出流
PrintWriter
PrintStream
构造器:
- public PrintStream(OutputStream os)
- public PrintStream(File file)
- public PrintStream(String filePath)
方法:
- public void print(XXX xxx)
PrintWriter
构造器:
- public PrintWriter(OutputStream os)
- public PrintWriter(File file)
- public PrintWriter(String filePath)
方法:
- public void print(XXX xxx)
PrintStream ps =new PrintStream("D://s。text");
Propreties
- 是一个Map集合,但是HashMap更好用
存放键值对的信息
Propreties ps =new Propreties();
ps.setPropreties("admin","123456");
ps.put("password","123456");
ps.store(new FileWriter("D://a.txt","give me 100"));
//路径,心得
//加载属性到Propreties对象中
ps.load(new FileReader("D://a.txt"));
ps.get("admin");
ps.getPropreties("admin");
IO框架
我醉了经典白学
commonis-io
FileUtils
- String readFileToString(File file,String encoding) 读取文件中的数据,返回字符串
- void copyFile(File srcfile,File destFile) 复制文件
- void copyDirectoryToDirectory(File srcDir,File destDir) 复制文件夹
NIO
Files.copy(Path.of(""),Path.of(""));
Files.deleteIfExists(Path.of(""))
多线程
- 多线程的创建
- Thread类的方法
- 线程安全、线程同步
- 线程通信、线程池
- 定时器、线程状态..
Thread类
- java是通过java.lang.Thread类来代表线程的
- 按照面向对象的思想,Thread类应该提供了实现多线程的方式
创建线程
方式一
1.定义一个线程类:extends Thread
public class MyThread extends Thread{
//2.重写run方法
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
public clss ThreadDemo1{
main(){
//3.new 一个新线程
Tread t =new MyThread();
//4.调用start方法
t.start();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
优缺点:
1.编码简单
2.线程类继承了Thread类不利于扩展
为什么用start()不用run()?
直接run()是普通方法执行,依然是一个主线程
不要把主线程的任务放到执行之前,还是单线程
方式二
- 创建一个线程任务实现接口Runnable接口,重写run()方法
- 创建线程类对象
- 把对象任务交给Tread处理
public MyThread implements Runnable{
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
public class a{
main(){
//创建一个任务对象
Runnable a = new MyThread();
//任务对象交给Thread处理
Thread t = new Thread(a);
t.start();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
Thread的构造器:
- public Thread(String name) 可以为当线程指定名称
- public Thread(Runnable target)分装Runnable对象为线程对象
- public Thread(Runnable target,String name)
优缺点:
- 实现接口,扩展性强
- 多一层封装,如果线程有执行结果是不可以直接返回的 run()没有返回值
匿名内部类:
public class a{
main(){
//创建一个任务对象
Runnable a = new Runable(){
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
};
//任务对象交给Thread处理
Thread t = new Thread(a);
t.start();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
方式三:JDK5.0 实现Callable接口和FutureTask
- 得到任务对象
- 实现Callable重写call(),
- 用FutureTask把Callable对象分装成线程的任务对象
- 把线程对象交给Thread
- 调用Thread的strat()启动线程
- 线程执行完毕后,通过FutureTask的get方法区获取任务执行的结果
pubilc class A implements Callable<String>{
private int n;
public A(n){
this.n=n;
}
@Override
public String Call() throw Exception{
String sum=0;
for(int i=0;i<n;i++){
sum+=i;
System.out.print(i);
}
return "结果是"+sum;
}
}
public class B {
main(){
Callable<String> c = new A(10);
FutureTask<String> f = new FutureTask<>(c);
//FutureTask实现了Runable接口
Thread t =new Thread(f);
t.start();
//没有结果会等待
try{
String s =f.get();
}catch(Excetion e){
e.printStackTrace();
}
}
}
优缺点:
- 实现接口扩展性强
- 可以得到结果
- 较为复杂
线程的常用方法
getName()获取线程名字
setName()修改线程名字
currentThread()获取线程的对象
定义一个线程类:extends Thread
public class MyThread extends Thread{
//2.重写run方法
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
public clss ThreadDemo1{
main(){
//3.new 一个新线程
Tread t =new MyThread();
//4.调用start方法
t.setName("1号线程")
t.start();
Thread m =Thread,currentThread();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
主线程的名称就是main
线程的休眠方法:技术优化--百度网盘
- public static void sleep(long time) 让当前的线程休眠,单位毫秒
main(){
for(int i =0;i<5;i++){
if(i==3){
//技术优化点
Thread.sleep(3000);
}
}
}
线程安全问题
- 存在多线程共享
- 同时访问共享资源
- 存在修改共享资源
public class ThreadDemo{
main(){
Account a =new Account ("郝泾钊",10000);
//小明小红
new DrawThread(acc,"小明").start();
new DrawThread(acc,"小红").start();
}
}
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
public class DrawThread extends Thread{
private Account acc;
public DrawThread(Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run(){
acc.drawMoney(1000);
}
}
线程同步
- 加锁
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
synchronized("heima"){
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
}
synchronized(){}:
- 锁用随机对象好不好? 不好 同步代码块
建议使用共享资源作为锁对象
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
synchronized(this){
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
}
- 实例方法用this
- 静态方法用类名.class
线程同步
方法上修饰synchronized
默认用this,但是代码要高度面向对象
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public synchronized void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
- 同步代码块好还是同步方法好?
- 同步代码块好,性能好
- 同步方法原理了?
- 也是用synchronized修饰默认是this
Lock锁
JDK5 加入的,更丰富功能,Lock接口不能实例化
- public ReentrantLock() 获得Lock的对象
方法:
- void lock() 获得锁
- void unlock() 释放锁
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
private final Lock lock =new ReentrantLock();
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
lock.lock();
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
lock.unlock();
}
}
线程通信
- 线程之间相互发送数据
- 共享的数据的情况决定自己做什么
常见模型:
- 生产者与消费者模型:
- 生产者线程产生数据,唤醒消费者;然后等待自己;消费者消费完数据之后唤醒生产者,然后等待自己。
方法:
Object类中:
- void wait() 让当前线程等待并释放占用锁,直到另一个线程调用notify或notifyAll方法
- void notify() 唤醒末个等待的线程
- void notifyAll()唤醒正在等待的所有线程
上述方法要用同步锁对象来调用
例子:
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public synchronized void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
//没钱了,唤醒别的线程
this.notifyAll();
this.wait();
}else{
//当前等待,唤醒别的线程 先叫醒别人在打晕自己
this.notifyAll();
this.wait();
}
}
public synchronized void deposit(double money){
//判断是谁来存钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money==0){
//这样更容易不安全
System.out.print(name+"来存钱成功,存钱"+money);
this.money+=moey;
//有钱了,唤醒别的线程
this.notifyAll();
this.wait();
}else{
//有钱不存钱
this.notifyAll();
this.wait();
}
}
}
main(){
Account acc =new Account("132",0);
//创建两个取钱线程
new DrawThread(acc,"小明");
new DrawThread(acc,"小红");
//创建两个存钱线程
new SaveThread(acc,"亲爹");
new SaveThread(acc,"亲爹");
new SaveThread(acc,"亲爹");
}
取钱:
public class DrawThread extends Thread{
private Account acc;
public DrawThread(Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run(){
while(true){
acc.drawMoney(1000);
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
存钱:
public class SaveThread extends Thread{
private Account acc;
public DrawThread(Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run(){
acc.deposit(1000);
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
线程池(重点)
- 创建线程的开销很大,----线程池解决
线程池的接口:ExecutorService
得到线程池对象
1.使用ExecutorService的实现类ThreadPoolExecytor自创建一个线程
ExecutorService------->ThreadPoolExecytor
2.使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程空闲时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略)
{
...
}
- corePoolSize 核心线程数,默认为1。
- 设置规则:
CPU密集型(CPU密集型也叫计算密集型,指的是运算较多,cpu占用高,读/写I/O(硬盘/内存)较少):corePoolSize = CPU核数 + 1
IO密集型(与cpu密集型相反,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。):corePoolSize = CPU核数 * 2
- 设置规则:
- maximumPoolSize
- 最大线程数,默认为Integer.MAX_VALUE 一般设置为和核心线程数一样
- keepAliveTime
- 线程空闲时间,默认为60s,一般设置为默认60s
- unit
- 时间单位,默认为秒
- workQueue
- 队列,当线程数目超过核心线程数时用于保存任务的队列。(BlockingQueue workQueue)此队列仅保存实现Runnable接口的任务。(因为线程池的底层BlockingQueue的泛型为Runnable)
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列作为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 采用就是 LinkedBlockingQueue,而博主踩到的就是这个坑,当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。
当然这种队列,maximumPoolSize 的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
有界队列
当使用有限的 maximumPoolSizes 时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。
同步移交队列
如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列
- 队列,当线程数目超过核心线程数时用于保存任务的队列。(BlockingQueue workQueue)此队列仅保存实现Runnable接口的任务。(因为线程池的底层BlockingQueue的泛型为Runnable)
- threadFactory
- 线程工厂,用来创建线程。
为了统一在创建线程时设置一些参数,如是否守护线程,线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。
它是一个接口类,而且方法只有一个,就是创建一个线程。
如果没有另外说明,则在同一个ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。
通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。
如果从newThread 返回 null 时ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务
- 线程工厂,用来创建线程。
- handler
- 拒绝策略,默认是AbortPolicy,会抛出异常。
当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务。
当线程池被调用shutdown()后,会等待线程池里的任务执行完毕再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务。
AbortPolicy 丢弃任务,抛运行时异常。
CallerRunsPolicy 由当前调用的任务线程执行任务。
DiscardPolicy 忽视,什么都不会发生。
DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务。
- 拒绝策略,默认是AbortPolicy,会抛出异常。
核心线程--临时线程
临时线程什么时候创建?
新任务提交任务是发现核心任务都在忙时,任务队列也满了,并且可以创建临时线程,此时会创建临时线程
什么时候会开始拒绝任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务会被拒绝。
线程池处理Runable、Callable任务
方法:
- void execute(Runnable command) 执行任务、命令,没返回值--一般Runable任务
- Future submit(Callable task)执行任务、命令,又返回值,--一般Callable任务
- void shutdown()等任务执行完毕后关闭线程
- List shutdownNow()立即关闭线程,停止执行的任务,并返回队列中未执行的任务
新任务的拒绝策略:
- ThreadPoolExecutor.AborPolicy 丢弃任务并抛出RejectedExccutionException默认
- ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常不推荐
- ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务,然后把当前任务加入到队列中
- ThreadPoolExecutor.CallerRunsPolicy由主线程负责调用任务的run()方法从绕过线程池执行
public class TreadPoll{
main(){
//创建线程池对象
ExcutorService pool =new ThreadPoolExcutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AborPolicy() )
}
//模拟线程处理
Runnable target =new MyRunnable();
pool.execute(target);
pool.execute(target);
pool.execute(target);
//任务队列
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//创建临时线程
pool.execute(target);
pool.execute(target);
//拒绝策略触发
pool.execute(target);
//关闭线程池(开发中一般不会使用)
pool.shutdownNow();
pool.shutdown();
}
public class MyRunnable implement Runnable{
@Override
public void run(){
for(int i =0 ;i<5;i++){
System.out.print(Thread.cuurentThread().getNmae()+"编写了hello world");
}
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
处理Callable任务
public class TreadPoll{
main(){
//创建线程池对象
ExcutorService pool =new ThreadPoolExcutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AborPolicy() )
}
Future<String> f1=pool.submit(new MyCallable(100));
Future<String> f2=pool.submit(new MyCallable(100));
Future<String> f3=pool.submit(new MyCallable(100));
String rs = f1.get();
String rs2 =f2.get();
String rs3 = f3.get();
}
Executors工具类
- public static ExecutorsService newCachedThreadPool() 线程池的数量随着任务的增加而增加,如果执行完毕空闲一段时间会被回收
- public static ExecutorsService newFixedThreadPool(int nThreads)创建固定的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新的线程替代它
- public static ExecutorsService newSingleThreadExecutor()创建一个线程池对象,如果线程出现异常而结束,那么线程池会补充一个新的线程
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
Executors底层也是实现 ThreadPoolExecutor实现的
main(){
ExecutorService pool=Executors.ExecutorsService newFixedThreadPool(3);
}
但是大型的并发项目会出现系统分险
- 内存溢出,线程的内存溢出
- 任务没有限制
定时器
方式一:Timer
方法二:newScheduledThreadPool
Timer
构造器:
- public Timer() 创建定时器对象
方法:
- public void schedule(TimerTask task,long delay, long period) 开启一个定时器执行task任务
问题:
- Timer是单线程,存在延时与定时器的时间有出入的情况
- 可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续的任务执行
main(){
Timer timer =new Timer();
timer.scedule(new TimerTask(){
@Oberride
public void run(){
//业务
System.out.print("定时器");
}
},3000,2000)
}//3秒延时调用2秒周期调用
newScheduledThreadPool;
JDK1.5引入的并发包
Executors
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
方法:
- public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initalDelay ,long period,TimeUnit unit) 周期调度方法
优点:
- 基于线程池执行,某个任务情况不会影响其他的定时任务的执行
main(){
ScheduledExecutorService pool =Executor.newScheduledThreadPool(3);
pool.scheduleAtFixedRate(new TimeTrask(){
@Override
public void run (){
//业务
}
},0,2,TimeUnit.SECONDS);
}//初始化延迟事件0秒 周期延迟2秒单位秒
并发和并行
多线程:并发和并行同时进行
生命周期
Thread类的枚举类的6中状态
- new 新建状态
- Runnable 可运行状态start()
- Blocked锁阻塞状态
- waiting无限等待状态wait()
- Timed Waiting (计时等待)
- sleep状态的线程好了不用强锁、、、我不要脸
- wait状态的线程的时间到了,并得到锁,可以跑
- wait状态时间没到,被唤醒,并得到锁可以跑
- wait没有得到锁会进入锁阻塞
- Teminated被终止状态
网络编程
- Client-Server(CS)
- Browser/Server(BS)
1.客户端--服务端
安装客户端
- 更新。
- 依赖PC
2.浏览器和服务端
- 分布式
- 兼容性
- 一站开发
网络通信:
- UDP 不确定在线 不做消息确认
- TCP可靠的通信
- 及时通信
- 模拟BS通信
三要素
- IP地址:设备在网络中的地址,唯一标识
- 端口:应用程序在设备中的唯一标识
- 协议:数据在网络中的传输规则,常见的有UDP协议和TCP协议
IPv4:
- 32位(4字节)
- 点分十进制
IPv6:
- 128位(16个字节),号称可以为地球上的每一粒沙子编号
- 冒分十六进制
域名
- 公网地址、私网地址(局域网使用)
- 192.168..开头就是局域网 192.168.0.0--192.168.255.255,专门内部使用
Ip命令:
- ipcofig:查看本机ip
- ping IP的地址:检查网络是否连通
特殊id:
- 127.0.0.1或者localhost:只会找本机
Ip地址操作类
InetAddress
- public static InetAddress getLocalHost() 返回主机的ip
- public static InetAddress getByName()得到指定的主机ip地址对象,参数是域名或者ip地址
- public String getHostName()获取此ip地址的主机名
- public String getHostAddress()返回ip地址的主机名
- public boolean isReachable(int timeout)在毫秒内连通该ip地址对应的主机,连通返回true
main(){
//1.获得本机ip对象
InetAddress ip = InetAddress .getLocalHost();
//得到域名
InetAddress ip = InetAddress .getByName("www.baidu.com");
//公网的ip
InetAddress ip = InetAddress .getByName("112.82.248.76");
端口
标识在计算机上运行的程序,规定的是一个16的二进制,0-65535.
端口类型:
- 周至端口:0-1023(HTTP:80,FTP:21)
- 注册端口:1024-49151(Tomcat:8080,MySQL:3306)
- 动态端口:49152-65535
协议
连接和通讯数据的规则--------网络通讯协议
- OSI参考模型:世界互联网协议规范
- TCP/IP参考模型:事实的国际标准
TCP:
- 应用层
- HTTP\FTP\DNS\SMTP
- 传输层
- TCP\UDP
- 网路层
- IP\ICMP
- 数据链路层+物理
- 物理寻址、比较流
传输层的协议:
- TCP:传输控制协议
- UCP:用户数据报协议
TCP:
- 采用TCP协议,必须双方先建立连接,它使面向连接的可靠通信协议
- 传输前,采用三次握手方式建立连接,所以是可靠的
- 在连接中可以进行大数据量的传输
- 连接、发送数据都需要确认、且传输完毕后、还要释放已建立的连接。通信效率较低
TCP协议的场景:
- 对信息安全要求较高的场景,文件下载,金融数据通信
TCP的三次握手
- 客户端向服务器发送请求--等待服务器确认
- 服务器向客户端返回了一个相应--告诉客户端接受到了请求
- 客户端向服务器再次发出确认信息---连接建立
TCP的四次挥手
- 客户端向服务器发出取消请求
- 服务器向客户端返回一个相应---表示收到客户端取消请求
- 服务器向客户端发出确认消息
- 客户端再次发出消息--连接取消
UDP:
- 一中无连接、不可靠的传输协议
- 将数据源ip、目的地ip、和端口封装成数据包、不需要建立连接
- 每个数据包的大小限制在64kb内
- 发送不管对方是否准备好,接收方也不确认,所以是不可靠的
- 可以发送广播、发送数据结束时无需释放资源、开销小、速度快
适合语音通话、视频会话
UDP
数据包:
构造器:
- public DatagramPacket(byte[] buf,int length, InetAddress,int port)port接受的端口
- public DatagramPacket(byte[] buf,int length) 创建接受端的数据包 buf储存的内容 length 能接受的长度
DatagramSocket发送端和接收端对象
构造器:
- public DatagramSocket()
- public DatagramSocket(int port)
方法:
- public void send( DatagramPacket dp) 发送数据包
- public void receive( DatagramPacket p)接收数据包
main(){
DatagramSocket sock =new DatagramSocket();
//数据包
byte[] buffer ="我是韭菜".getBytes();
DatagramPacket packet =new DatagramPacket(buffer,buffer.length,InetAddress.getLocalHost(),8888);
sock.send(packet);
sock.close();
}
main(){
DatagramSocket sock =new DatagramSocket(8888);
//数据包
byte[] buffer =new byte[1024*64];
DatagramPacket packet =new DatagramPacket(buffer,buffer.length);
sock.receive(packet);
String s =new String(buffer);
socket.close();
}
多发多收
以后吧累了
TCP
面向连接,安全,可靠
java.net.Socket
Socket:
- public Socket(String host,int port) 创建Socket对象与服务器连接,参数为服务器的ip和端口
方法:
- OutputStream getOutputStream()获得字节输出流
- InputStream getInputStream() 获得字节输入流
main(){
try{
//创建Socket管道建立连接
Socket socket =new Socket("127.0..0..1",7777);
//得到字节输出流
getOutputStream is =socket.getOutputStream();
//变成高级流
PrintStream ps =new PrintStream(is);
//发送消息
ps.print("约么");
ps.flush();
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
ServerSocket(服务端)
- public ServerSocket(int port) 注册服务端
main(){
try{
//创建ServerSocket管道建立连接
ServerSocket ss =new ServerSocket(7777);
Socket socket =ss.accept();
//得到字节输出流
getInputStream is =socket.getInputStream();
//变成高级流
BufferedReader br =new BufferedReader(new InputStreamReader(is));
//收消息
while(ms=br.readLine()!=null){
System.out.print(socket.RemoteSocketAddress()+"说了"+ms)
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
多发多收
- 客户端
main(){
try{
//创建Socket管道建立连接
Socket socket =new Socket("127.0..0..1",7777);
//得到字节输出流
getOutputStream is =socket.getOutputStream();
//变成高级流
PrintStream ps =new PrintStream(is);
Scanner sc =new Scanner(System.in);
//发送消息
while(true){
System.out.print("请说");
String ms = sc.nextLine();
ps.println(ms);
ps.flush();
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
ServerSocket(服务端)
- public ServerSocket(int port) 注册服务端
main(){
try{
//创建ServerSocket管道建立连接
ServerSocket ss =new ServerSocket(7777);
Socket socket =ss.accept();
//得到字节输出流
getInputStream is =socket.getInputStream();
//变成高级流
BufferedReader br =new BufferedReader(new InputStreamReader(is));
//收消息
while(ms=br.readLine()!=null){
System.out.print(socket.RemoteSocketAddress()+"说了"+ms);
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
但是服务daunt不可以接受多个客户端信息。
多客户端
- 客户端
main(){
try{
//创建Socket管道建立连接
Socket socket =new Socket("127.0..0..1",7777);
//得到字节输出流
getOutputStream is =socket.getOutputStream();
//变成高级流
PrintStream ps =new PrintStream(is);
Scanner sc =new Scanner(System.in);
//发送消息
while(true){
System.out.print("请说");
String ms = sc.nextLine();
ps.println(ms);
ps.flush();
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
ServerSocket(服务端)
- public ServerSocket(int port) 注册服务端
main(){
try{
//创建ServerSocket管道建立连接
ServerSocket ss =new ServerSocket(7777);
//收消息
while(ms=br.readLine()!=null){
Socket socket =ss.accept();
new SerberThread(socket).start();
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
线程:
public class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket-socket;
}
@Override
public void run(){
try{
//得到字节输出流
getInputStream is =socket.getInputStream();
//变成高级流
BufferedReader br =new BufferedReader(new InputStreamReader(is));
//收消息
while(ms=br.readLine()!=null){
System.out.print(socket.RemoteSocketAddress()+"说了"+ms);
}
//socket.close();不建议关闭流
}catch{
e.printStrackTrace();
}
}
}
线程池优化
- 客户端
main(){
try{
//创建Socket管道建立连接
Socket socket =new Socket("127.0..0..1",7777);
//得到字节输出流
getOutputStream is =socket.getOutputStream();
//变成高级流
PrintStream ps =new PrintStream(is);
Scanner sc =new Scanner(System.in);
//发送消息
while(true){
System.out.print("请说");
String ms = sc.nextLine();
ps.println(ms);
ps.flush();
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
ServerSocket(服务端)
- public ServerSocket(int port) 注册服务端
//定义线程池
private Static ExecutorService pool =new ThreadPoolExecutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue(2,Executor.defaultThreadFactorty(),new ThreadPoolExcutor.AbortPolicy()));
main(){
try{
//创建ServerSocket管道建立连接
ServerSocket ss =new ServerSocket(7777);
//收消息
while(ms=br.readLine()!=null){
Socket socket =ss.accept();
Runner a =new SerberThread(socket);
pool.excute(a);
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
线程:
public class ServerThread implement Runnable{
private Socket socket;
public ServerThread(Socket socket){
this.socket-socket;
}
@Override
public void run(){
try{
//得到字节输出流
getInputStream is =socket.getInputStream();
//变成高级流
BufferedReader br =new BufferedReader(new InputStreamReader(is));
//收消息
while(ms=br.readLine()!=null){
System.out.print(socket.RemoteSocketAddress()+"说了"+ms);
}
//socket.close();不建议关闭流
}catch{
e.printStrackTrace();
}
}
}
优点:
- 适合通信时长较短的案例
即时通讯
- 客户端
main(){
try{
//创建Socket管道建立连接
Socket socket =new Socket("127.0..0..1",7777);
//创建一个读线程
new ClienThread(socket).start();
//得到字节输出流
getOutputStream is =socket.getOutputStream();
//变成高级流
PrintStream ps =new PrintStream(is);
Scanner sc =new Scanner(System.in);
//发送消息
while(true){
System.out.print("请说");
String ms = sc.nextLine();
ps.println(ms);
ps.flush();
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
客户端的线程
public class ClienrThread implement Runnable{
private Socket socket;
public ClienThread(Socket socket){
this.socket-socket;
}
@Override
public void run(){
try{
//得到字节输出流
getInputStream is =socket.getInputStream();
//变成高级流
BufferedReader br =new BufferedReader(new InputStreamReader(is));
//收消息
String line;
while(line=br.readLine()!=null){
System.out.print(socket.RemoteSocketAddress()+"说了"+line);
}
//socket.close();不建议关闭流
}catch{
e.printStrackTrace();
}
}
}
ServerSocket(服务端)
- public ServerSocket(int port) 注册服务端
//定义线程池
private Static ExecutorService pool =new ThreadPoolExecutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue(2,Executor.defaultThreadFactorty(),new ThreadPoolExcutor.AbortPolicy()));
//客户端的留言
public static List<Socket> allSocket = new ArrayList<>();
main(){
try{
//创建ServerSocket管道建立连接
ServerSocket ss =new ServerSocket(7777);
//收消息
while(ms=br.readLine()!=null){
Socket socket =ss.accept();
allStock.add(socket);
Runner a =new SerberThread(socket);
pool.excute(a);
}
//socket.close();不建议关闭流
}catch(Exception e){
e.printStackTrace();
}
}
线程:
public class ServerThread implement Runnable{
private Socket socket;
public ServerThread(Socket socket){
this.socket-socket;
}
@Override
public void run(){
try{
//得到字节输出流
getInputStream is =socket.getInputStream();
//变成高级流
BufferedReader br =new BufferedReader(new InputStreamReader(is));
//收消息
while(ms=br.readLine()!=null){
System.out.print(socket.RemoteSocketAddress()+"说了"+ms);
sendMssageToAll(line);
}
//socket.close();不建议关闭流
}catch{
e.printStrackTrace();
}
}
private void sendMssageToAll(String msg){
for(Socket socket:ServlerThread.allSocket){
PrintStream ps =new PrintStream(socket.getOutputStream());
ps.println(msg);
ps.flush();
}
}
}
终于最后了2022.5.7 15:46
单元测试
- 最小的功能单元编写测试代码,java针对方法,检查方法的正确性
JUnit单元测试框架
@Test注解
public class A {
@Test
public void a(){
.....
}
}
- 必须导入jar包
- 定义的测试方法必须是无参数无返回值的,且公开的方法
- 测试的方法要用@Test注解
注解
- @Test
- @Before 实例方法,每个测试方法之前执行
- @After 实例方法,每个测试方法之前执行
- @BeforeClass 静态方法,所有测试方法之前执行一次
- @AfterClass静态方法,在所有测试方法之后执行一次
初始化资源
释放资源
反射
是指对于任何一个Clss类,在运行是都可以直接得到这个类的全部成分
- 类构造对象:Constructor
- 类的成员变量对象:Filed
- 类的成员方法对象:Method
- 动态获取类的信息以及动态的调取类中的成分的能力称为java语言的反射机制
Class c = A.class;
获得类对象
Class类中的静态方法:
- forName(String className)
- 类名.class
- 对象.getClass()
main(){
Class c =Class.forName("com.hjz.entity.Student");
Class c =Student.class;
Student s =new Student();
Class c = s.getClass();
}
获得构造器对象:
1.获得Class类对象
2.获得构造器对象
- Constructor<?> getConstructors()返回所有的构造器数组(只拿public)
- Constructor<?> getDeclredConstructors()返回所有的构造器数组,有就能拿到
- Constructor getConstructors( Class<?>... paramerTypes)返回单个构造器
- Constructor getDeclredConstructors( Class<?>... paramerTypes)
main(){
Class c =Student.class;
Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclredConstructors(String.class,int.class);//有一个构造方法是啥用啥
}
创建对象:
- T newInstance(Object...initargs) 指定的构造器创建对象
- public void setAccesible(boolean flag) 设置为true 表示取消访问检查,进行暴力反射
main(){
Class c =Student.class;
Constructor cs = c.getDeclredConstructors();//有一个构造方法是啥用啥
//反射会破坏封装性
cs.setAccesible(true);//是私有的
Student s = (Student)cs.new newInstance();
}
获得成员变量对象:
1.获得Class类对象
2.获得成员变量对象
- Filed[] getDeclredFileds()
- Filed getDeclredFiled(String name)
方法:
- getType()
main(){
Field agef =c.getDeclredFiled("age");
agef.setAccessible(true);
Student s =new Student();
agef.set(s.18);
int age = (int) agef.get(s);
}
获得方法对象
- Method[] getMothod()
- Method getDeclredConstructor( ,String name,Class<?>... paramerTypes)
....和之前的一样
反射的作用
- 反射是在运行时使用的技术,此时集合泛型就不能产生约束了,此时是可以为集合存入其他任何类型的元素的
ArrayList<Integer> list =new ArrayList<>();
实际跑起来都是Arraylist的类型了,不用管Integer了
- 反射的通用框架
1.定义一个方法可以接受任意的对象
2.每次收到一个对象后,解析这个对象的全部成员变量
3.这个对象是任意的
4.遍历这个成员变量,提取成员变量的具体的值
5.把名称和值放到成员变量对应的文件里面
注解
JDK5 引入的标注
- 对java的类、方法、对象有特殊处理
自定义注解
public @interface 注解名称{
public属性名称 属性名()default 默认值;
}
例子:
public @interface MyBook{
String name();
String[] authors();
double price();
}
public class a{
@MyBook(name="java",autors={"黑马","我的"},price=200)
public void ss(){
}
}
只有一个value属性可以写可以不写
元注解
注解上的注解
@Target:约束自定义注解只能在哪使用
发现ElementType是个枚举。属性作用如下:
TYPE:接口、类、枚举
FIELD:字段、枚举的常量
METHOD:方法
PARAMETER:方法参数
CONSTRUCTOR:构造函数
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解
PACKAGE:包@Retention:申明注解的生命周期
按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBook{
String name();
String[] authors();
double price();
}
注解解析
- Annotation:注解的顶级接口,注解都是Annotation的类型对象
- AnnotatedElement:给接口定义了注解解析相关的解析方法
动态代理(重点)
代理为对象的行为实现一些辅助操作:
有时间看吧
xml
xml解析---xpath解析技术
可扩展标记语言
数据表示格式
<?xml version="1.0" encoding="utf-8">
<--注释有且仅有一个-->
- <小于
- >大于
- & 和
- &apos 单引号
- " 引号
文档约束
- DTD
- schema
解析数据技术:Dom4J框架底层技术
- SAX解析
- DOM解析
数据检索技术XPath
- Dom4J需要进行全文检索,然后寻找资源
- Xpath技术适合数据检索
完结了我草终于2022.05.07
java基础(完整版)的更多相关文章
- HTML5+JavaScript动画基础 完整版 中文pdf扫描版
<HTML5+JavaScript动画基础>包括了基础知识.基础动画.高级动画.3D动画和其他技术5大部分,分别介绍了动画的基本概念.动画的JavaScript基础.动画中的三角学.渲染技 ...
- 最新java学习路线:含阶段性java视频教程完整版
最新java学习路线:带阶段性java视频教程版本 第一阶段:Java基础 学习目标: 掌握基本语法.面向对象.常用类.正则.集合.Io流.多线程.Nio.网络编程.JDK新特性.函数式编程 知识点细 ...
- SSM Spring SpringMVC Mybatis框架整合Java配置完整版
以前用着SSH都是老师给配好的,自己直接改就可以.但是公司主流还是SSM,就自己研究了一下Java版本的配置.网上大多是基于xnl的配置,但是越往后越新的项目都开始基于JavaConfig配置了,这也 ...
- Java基础(二十三)GUI图形界面编程(Java基础完)
这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...
- Java入土--Java基础(二)
Java基础(二) 接上一讲,我们接着来聊聊Java的一些基础知识,下一讲就会进行流程的控制. 类型转换 首先呢,是类型的转换,接上一个内容的数据类型,类型转换就是数据类型更进一步的应用. 由于Jav ...
- Java编程思想(第4版) 中文清晰PDF完整版
Java编程思想(第4版) 中文清晰PDF完整版 [日期:2014-08-11] 来源:Linux社区 作者:Linux [字体:大 中 小] <Java编程思想>这本书赢得了全 ...
- Java-2-学习历程2:基础知识1,2,3文档、完整版视频资源、电子书籍下载
Java学习历程:基础知识1,2,3文档.完整版视频资源.电子书籍 1.基础知识1,2.3可到下面地址下载: http://download.csdn.net/detail/iot_li/886 ...
- JAVA在线观看视频教程完整版
今天给大家介绍一下JAVA在线观看视频教程完整版,我们知道Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语 ...
- 老王Python培训视频教程(价值500元)【基础进阶项目篇 – 完整版】
老王Python培训视频教程(价值500元)[基础进阶项目篇 – 完整版] 教学大纲python基础篇1-25课时1.虚拟机安装ubuntu开发环境,第一个程序:hello python! (配置开发 ...
- JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版下载
JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版下载JDK(Java Development Kit,Java开发包,Java开发工具)是一个写Java的applet和 ...
随机推荐
- 【云原生 · Kubernetes】Kubernetes 编排部署GPMall(一)
1.规划节点 IP 主机名 节点 10.24.2.156 master Kubernetes master 节点 10.24.2.157 node Kubernetes worker 节点 2.基础准 ...
- IDEA项目下out与target目录的区别详解
IDEA项目下out与target目录的区别详解 一.目录主要区别: out存放的是该项目下所有Module(模块)的编译结果. target存放的是单个Module的编译结果. 二.目录详解 out ...
- 【每日一题】【找到位置返回&升序数组中第K大就是n-K小】2022年1月17日-NC88 寻找第K大
描述有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数. 给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在. 方法 ...
- 【每日一题】2021年12月6日-剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点. 例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1.2.3.4.5.6 ...
- Redis如何模糊匹配Key值
Redis模糊匹配Key值 使用Redis的scan代替Keys指令: public Set<String> scan(String matchKey) { Set<String&g ...
- Qt操作Json小结
Qt操作Json 1.QJsonDocument 1.详细说明 QJsonDocument类提供了读写JSON文档的方法. QJsonDocument是一个封装了完整JSON文档的类,可以从基于UTF ...
- 服务器迁移遇到的bug
目前有个客户做了个下单系统,系统运行了太多的扩展了,但是又没有文档,我就想着能不能把服务器打包成镜像,然后用新服务直接使用?? 事实是完全OK的.服务器用的阿里云. 但是遇到一个bug,我目前还没解决 ...
- 下载kali以及安装
一.kali下载 下载地址:https://www.kali.org/get-kali/#kali-virtual-machines 二.kali安装 1.打开VMware,打开文件,选择刚刚下载并解 ...
- java顺序数组插入元素
本文主要阐明已知顺序数组,在数组中插入一个数据元素,使其仍然保持有序. 关键是寻找num在原数组中插入的位置: 当num在原数组中是最大的情况,num应该插入到原数组的末尾. 否则,应该遍历原数组,通 ...
- 解决MySQL Connector/ODBC驱动无法安装Error1918
1.问题描述 我在一台windows服务器上安装好mysql之后,再安装mysql的ODBC连接驱动时,报错如下: 2.解决方法 之所以出现安装失败是由于缺少Miscrosoft Visual C++ ...