JavaSE基础知识汇总
1、Java基础
函数与类前面的修饰符示例
控制结构示例
包含的内容有:for循环、while循环、switch语句,if语句
public static void main(String[] args) {int a=0;for (int i = 0; i <10 ; i++) {a+=1;}while (a>0){if(a%2==0){System.out.println(a+"是偶数");}else{System.out.println(a+"是奇数");}a-=1;}switch(a){case 0:System.out.println("a是"+0);break;case 1:System.out.println("a是"+1);break;}
}
主数据类型与引用
primitive主数据类型+引用=变量
primitive的八种主数据类型
类型 | 位数 | 值域 |
---|---|---|
boolean | JVM决定 | true或false |
char | 16 bits | 0~65535 |
byte | 8 bits | -128~127 |
short | 16 bits | -32768~32767 |
int | 32 bits | ±2147483648 |
long | 64 bits | - 很大 ~ + 很大 |
float | 32 bits | 范围规模可变 |
double | 64 bits | 范围规模可变 |
注意,整数默认为int类型,小数默认为double类型,在创建其他类型变量以及运算时要标明。
float a=1.5f;//这个f不写则1.5默认为double,然后由double转为float
当大杯转小杯时可能会导致数据溢出,因此要注意数据的范围,以此来选择数据类型。
当编译器无法识别能否进行数据转化时,需要进行强制转换。
public class test {public static void main(String[] args) {long a=123;//int b=a;会报错,编译不通过int b=(int) a;System.out.println(a);}
}
自动强制转换的规则:
引用reference
如果一个变量是类类型,而非基本类型,那么该变量又叫引用。
可以把实例化的对象的reference当做一个遥控器,远程遥控其他的行为。
当实例化一个对象的时候,会创建一个遥控器,遥控器在栈区,对象在堆区。
Book b=new Book();//new Book();只是创建一个对象,前面的b表示引用指向他,等号表示指向
Book c=new Book();
Book d=c;//这时候c和d两个遥控器遥控一个对象
c=b;//这时候c和b遥控同一个对象,但是d仍然遥控自己的那个对象
作用域
- 声明在类的外面,变量就叫做字段 或者属性、成员变量、Field ,其作用域就是从其声明的位置开始的整个类
- 变量是参数:作用域在方法中
- 方法的局部变量:在方法中创建的变量作用域在方法内部
运算符
逻辑运算符
运算符 | 描述 |
---|---|
&& | 与 |
|| | 或 |
!= | 不等于 |
! | 非 |
短运算符
比如&&,当两端都为真才得到真,当左边为假便不会再计算右边
长运算符
比如&,强制虚拟机必须做两端的操作
算数运算符
符号 | 描述 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
++ | 自增 |
– | 自减 |
关系操作符
符号 | 描述 |
---|---|
> | 大于 |
>= | 大于或等于 |
< | 小于 |
<= | 小于或等于 |
== | 是否相等 |
!= | 不等于 |
位运算符
参见ACM——位运算符整理
用的不多,用来提高运算速度,可以忽略不学。
赋值运算符
符号 | 描述 |
---|---|
+= | a=a+x |
-= | a=a-x |
*= | a=a*x |
/= | a=a/x |
%= | a=a%x |
&= | a=a&x |
|= | a=a|x |
^= | a=a^x |
>>= | a=a>>x |
<<= | a=a<<x |
三元操作符
(判断)?A:B;
当判断的内容为真时返回A,否则返回B
int k = i < j ? 99 : 88;
数组
创建与初始化数组
数组是一个固定长度,包含相同类型数据的容器,具有默认值
public class demo extends test{public static void main(String[] args) {int []a=new int[5];//或者是写int a[]=new int[5];int []b = new int[]{100,102,444,836,3236};a[0]=15;System.out.println(a[0]);System.out.println(a.length);}
}
复制数组与合并数组
或者使用Arrays类的方法,或者是
复制数组: System.arraycopy(src, srcPos, dest, destPos, length)
- src: 源数组
- srcPos: 从源数组复制数据的起始位置
- dest: 目标数组
- destPos: 复制到目标数组的起始位置
- length: 复制的长度
public static void main(String[] args) {int a [] = new int[]{18,62,68,82,65,9};int b[] = new int[3];//分配了长度是3的空间,但是没有赋值//通过数组赋值把,a数组的前3位赋值到b数组System.arraycopy(a, 0, b, 0, 3);//把内容打印出来for (int i:b) {System.out.print(b[i] + " ");}
}
初始化二维数组与不规则长度数组
public static void main(String[] args) {//初始化二维数组,int[][] a = new int[2][3]; //有两个一维数组,每个一维数组的长度是3a[1][2] = 5; //可以直接访问一维数组,因为已经分配了空间//只分配了二维数组int[][] b = new int[2][]; //有两个一维数组,每个一维数组的长度暂未分配b[0] =new int[3]; //必须事先分配长度,才可以访问b[0][2] = 5;//指定内容的同时,分配空间int[][] c = new int[][]{{1,2,4}, {4,5}, {6,7,8,9}};
}
增强for循环
格式:可以通过values.for
快捷键实现
for (int each : values) {System.out.println(each);//do something
}
Arrays类的常用方法
包:import java.util.Arrays;
常用方法:
方法 | 描述 | 作用 |
---|---|---|
int[] b = Arrays.copyOfRange(a, 0, 3); | a是原数组,0取的到,3取不到 | 数组复制 |
String content = Arrays.toString(a); | a是数组,得到[18, 62, 68, 82, 65, 9] | 转换为字符串 |
Arrays.sort(a); | a是数组 | 升序排序 |
Arrays.binarySearch(a, 62 ):
|
62是要搜索的元素 | 搜索,前提是升序排列了,不存在返回负数 |
Arrays.equals(a, b) | ab为两个数组 | 判断两个数组是否相同 |
Arrays.fill(a, 5 );
|
填充a数组全为5 | 填充(初始化) |
创建类的数组
Dog[] myDogs=new Dog[3];
myDogs[0]=new Dog();//放咖啡的杯子就只能放咖啡
myDogs[0].name="Fido";
myDogs[0].bark();
初识类与对象——基本概念
封装数据
在创建数据时,标明private,否则实例化的对象可以直接修改对应的值,把内裤给别人看了就挺尴尬的…
可以创建对应的方法来获取或者设置相应数据,比如
public void setname(String newname){name=newname+getname();
}
实例变量的默认值
当你仅仅只是声明一个变量,但是不初始化的时候,java会默认给你赋一个值
数据类型 | 默认值 |
---|---|
integers | 0 |
floating points | 0.0 |
boolean | false |
references | null |
实例变量与局部变量的差别
实例变量是声明在类里面的,局部变量是声明在类的方法里的,局部变量在使用前必须要先赋值,否则编译器会报错。
可变长参数
在参数位置加三个点,例如
public void attack(Hero ... heros){for (Hero:heros) {//do something}
}
//传参时参数用逗号隔开
构造函数
声明为public,构造函数可以重载哦,注意别写void ,否则就成了普通方法
class demo {public demo(String a){System.out.println("调用了构造函数参数是"+a);}public demo(){System.out.println("调用了默认构造");}public static void main(String[] args) {demo test1=new demo("草");demo test2=new demo();}
}
this关键字
指向当前类本身的元素,类似c++,即使是同名的,this.name=name;
也可以赋值,称为隐式调用。
但是只要和属性不同名,直接赋值也OK的,this还能实现构造方法
的相互调用
一个包(package)内的class可以互相访问
要访问别的class用import导入哦,import package.class
成员变量四种权限修饰符
标黄了就表示不能访问到,不写默认为protected
自身 | 同包子类 | 不同包子类 | 同包类 | 其他类 | |
---|---|---|---|---|---|
private | 访问 | 继承 | 继承 | 访问 | 访问 |
protected | 访问 | 继承 | 继承 | 访问 | 访问 |
public | 访问 | 继承 | 继承 | 访问 | 访问 |
package | 访问 | 继承 | 继承 | 访问 | 访问 |
属性初始化
对象属性初始化有3种
- 声明该属性的时候初始化 public String name =
"some hero"
; - 构造方法中初始化
- 使用set方法
- 初始化块===>大括号括起来,不常用,别几把了解了
类的封装
封装概述
封装是面向对象三大特征之一(封装、继承、多态),是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
封装原则
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作与访问,成员变量设置为private
,提供对应的getAbb
和setAbb
方法
类的继承
子类继承父类可以获得父类的属性和方法,一个父类可以被多个子类继承,但是一个子类只能继承一个父类,不过一个类可以实现多个接口,Object类是所有类的父类,类的继承关键字是extents
继承中变量访问顺序
在子类方法中寻找一个变量时如下顺序:
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲)
继承中构造方法的访问顺序
子类中的所有构造方法都会默认先访问父类的无参构造方法
,子类会继承父类的数据,因此在子类初始化前一定要先进行父类初始化,每一个子类第一句话默认都是super()
继承中成员方法的访问顺序
顺序如下:先当前再长远
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲)
super关键字
- 调用父类带参构造方法
实例化子类的时候,会先调用父类的构造方法,当多个构造方法时,默认调用父类无参构造方法
如果想调用父类带参构造,需要用super,例如
super(name);//在ADHero带参构造下写,这个相当于调用参数为name的父类构造方法
- 调用父类属性
super.speed;//父类的属性
- 调用父类的方法
super.useItem();//父类的方法
this与supper的区别
this是访问或调用本类的成员变量或方法,supper是操作父类的
类的初始化顺序
父类静态变量 > 父类静态初始块 > 子类静态变量 > 子类静态初始块 > 父类成员变量 > 父类非静态初始块 > 父类构造器 > 子类成员变量 > 子类非静态初始块 > 子类构造器
方法(重写)覆盖
当子类需要父类的功能,而功能主题子类有自己特有内容时,可以重写父类的方法,这样既沿袭了父类的功能,又定义了子类独有的内容。如果在子类中定义一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配,就说子类的方法覆盖了父类的方法。简单来说,子类的方法和父类一模一样
就说覆盖,注解是@Override
四个特点:
- 有继承关系的两个类
- 具有相同方法名、返回值类型、形式参数列表
- 子类访问权限不能更低
- 抛出异常不能更多
四个注意事项:(多态语法)
- 方法覆盖只是针对于方法,和属性无关。
- 私有方法无法覆盖
- 构造方法不能被继承,所以构造方法也不能被覆盖。
- 方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义
类的多态
多态是同一对象在不同时刻表现出来的不同形态
多态的形式:
- 具体类多态
- 抽象类多态
- 接口多态
多态的前提和体现:
- 有继承/实现关系
- 有方法重写
- 有父类(接口)引用指向子(实现)类对象
操作符的多态:
- 加号两边都是数字代表数字相加
- 加号两边有一个字符串则表示字符串拼接
类的多态:
- 子类父类转型
/**
LifePotion(血瓶)和MagicPotion(魔瓶)都继承于Item,当使用effect方法时只需要传入Item对象即可,
不用再写useLifePotion和useMagicPotion两个方法
*/
public class Hero {public String name;protected float hp;public void useItem(Item i){i.effect();}public static void main(String[] args) {Hero garen = new Hero();garen.name = "盖伦";LifePotion lp =new LifePotion();MagicPotion mp =new MagicPotion();garen.useItem(lp);garen.useItem(mp);}
}
多态的好处与弊端
多态的优点:
- 提高了程序的扩展性
多态的弊端:
- 不能使用子类的特有功能
多态中转型
instanceof判断引用是否指向对象
子类转父类,向上:ADHero引用转Hero引用是完全没问题的
父类转子类,向下:需要用到子类的类名进行强制转化,为什么要加呢,因为不知道子类的引用指向的对象是谁,
注:父类转子类之后再转子类可能会报错
多态转换的顺序
抽象类(abstruct)
介绍
只对方法进行声明,而不去实现,在java中,一个没有方法体的方法称为抽象方法,如果类中有抽象方法,该类必须定义为抽象类
抽象类以及子类的特点
- 抽象类和抽象方法必须使用
abstruct
进行修饰 - 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类不能实例化,但是可以通过子类对象实例化,这叫抽象类的多态
- 抽象类的子类要么重写抽象类中所有的抽象方法,要么是抽象类
- 抽象是多态的表现形式之一
抽象类的成员特点
- 抽象类不能被实例化
- 成员变量既可以是变量也可以是常量(final)
- 构造方法可以有,作用是子类访问父类进行数据的初始化
- 成员方法可以是抽象的也可以是非抽象的,非抽象的方法提高了代码的复用性
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法
抽象类名作为形参和返回值
- 方法形参是抽象类名时,其实需要的是
该抽象类的子类对象
- 方法返回值是抽象类名时,其实返回的时
该抽象类的子类对象
接口(interface)
介绍
接口就像一种公共规范,只要符合规范标准,大家就可以通用。Java中的接口更多体现在对行为的抽象
接口的特点
- 接口用
interface
修饰 - 类实现接口用
implements
表示 - 接口不能实例化
- 接口是多态的表现形式之一
接口的成员特点
成员变量只能是常量,默认修饰符:
public static final
接口没有构造方法,因为接口更多的是体现在对行为的抽象
成员方法只能是抽象方法
,默认修饰符:public abstruct
JDK8之后接口都有了默认实现,可以在实现的类中直接调用
设计接口
接口就像约定,接口定义某些方法,交给某个类去实现,一个类可以实现多个接口 用逗号隔开就行
接口定义关键字:interface
public interface AD{public void physicAttack();
}
类去实现接口的关键字:implements
public class ADHero extends Hero implements AD{public void physicAttack(){System.out.println("发起进攻");}
}
注意,假如类不实现接口约定的方法,这个类会报错。
类和接口的关系
- 类和类的关系:继承关系,只能是单继承,但是可以多层继承
- 类和接口的关系:实现关系,类去实现接口,可以单实现,也可以多实现
- 接口和接口的关系:继承关系,可以单继承也可以多继承
注:可以继承一个类时同时实现多个接口
抽象类与接口的区别
# 成员区别抽象类---->常量和变量,可以定义四种访问权限,有构造方法,有抽象方法,也有非抽象方法接口------>只有公共的静态的常量和抽象方法
# 关系区别抽象类---->只能被单继承接口------>可以被类多实现(被其他接口多继承)
# 设计理念区别抽象类---->抽象类是对类抽象,是对根源的抽象,包括属性和行为,抽象类反映的是is-a关系,表示这个对象是什么接口------>主要是对行为动作的抽象,接口反映的是like-a关系,表示这个对象能做什么,接口是更加纯粹的抽象。
注:最大的相同点是抽象类和接口都不能直接实例化
接口名作为形参和返回值
- 方法的形参是接口名,其实需要的是
该接口的实现类对象
- 方法的返回值是接口名,其实需要的是
该接口的实现类对象
默认方法(Java8)
例如我们想升级接口,但是如果升级的话所有的实现类都需要实现这个新方法,可以使用一个新接口继承这个接口,然后找类去实现它,但是太麻烦了,于是有了默认实现方法
public interface Test{public default void show() {//public可以省略,但是default不行System.out.println("默认方法执行了,冲!");}
}
静态方法(Java8)
静态方法只能被接口调用,这样做的目的是防止实现多个接口的时候,这多个接口存在同一个静态方法,导致混乱
public interface Test{public static void show() {System.out.println("静态方法执行了,冲!");}
}
public class TestInterface{public void main(String[] args) {Test.show();}
}
私有方法(Java9)
Java 9中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔:Java8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法
,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java9增加私有方法的必然性
定义的格式:
- 格式一:
private 返回值类型 方法名(参数列表)
- 格式二:
private static 返回值类型 方法名(参数列表)
Java中的内部类
内部类介绍
内部类就是在类中定义一个类,例如在类A中定义了类B,这个类B就是内部类,
按照内部类在类中定义的位置不同,可以分为两种形式:
- 在类的成员位置:成员内部类,定义在类中
- 在类的局部位置:局部位置类,定义在类方法中
内部类的定义格式如下:
public class ClassNameA{修饰符 class ClassNameB{}
}
内部类:java文件的class里面的类,属性和方法平等的位置
内部类与外部类的访问特点
- 内部类可以直接访问外部类的所有属性和方法
- 外部类如果想访问内部类成员必须先创建对象
成员内部类
如何访问内部类呢?
外部类名.内部类名 对象名 = 外部类名.内部类名
通常情况下,我们把类做成内部类的目的就是不让别人访问到,因此通常是将内部类修饰为private
,外部类定义一个方法去调用内部类的函数,我们只需要调用外部对象的该方法即可
局部内部类
如果想调用局部内部类只能是在方法中创建内部类对象,然后使用内部类对象去访问方法
public class Test {public int num = 10;public void method() {class Inner {public void show(){System.out.println(num);}}Inner inner = new Inner();inner.show();}public static void main(String[] args) {Outer outer = new Outer();outer.method();}
}
非静态内部类
通过new 外部类().new 内部类()
的形式去使用
Hero garen = new Hero();
BattleScore score = garen.new BattleScore();
静态内部类
static修饰,就不用再创建外部类
Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
匿名内部类(是局部内部类一种)
前提是存在一个类,这个类可以是具体类也可以是抽象类,格式如下:
xxxx = new ClassNameOrInterfaceName(){//重写方法
}
Hero是一个抽象类(abstruct),重写方法表示继承了这个类或实现了这个接口
Hero hero = new Hero(){//实现一个attack新方法public void attack() {System.out.println("新的进攻手段");}
};
hero.attack();
///--------也可以------//
new Hero(){//实现一个attack新方法public void attack() {System.out.println("新的进攻手段");}
}.attack();
匿名内部类的本质是继承了该类或实现了该接口的匿名对象
内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置
本地类
本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
public abstract class Hero {String name; //姓名float hp; //血量float armor; //护甲int moveSpeed; //移动速度public abstract void attack();public static void main(String[] args) {//与匿名类的区别在于,本地类有了自定义的类名class SomeHero extends Hero{public void attack() {System.out.println( name+ " 新的进攻手段");}}SomeHero h =new SomeHero();h.name ="地卜师";h.attack();}
}
基本类型的包装类
基本数据类型与包装类对应关系
基本类型转封装类
//一个int类型数据
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
封装类转基本类型
//一个封装类型
Integer it = new Integer(5);
//封装类型转换成基本类型
int i2 = it.intValue();
自动装箱
基本数据类型通过等号转换为包装类,称为装箱
int i = 5;
//自动装箱为Integer类型
Integer it2 = i;
自动拆箱
包装类通过等号转为基本数据类型,称为拆箱
Integer it = new Integer(5);
//自动拆箱为int基本数据类型
int i3 = it;
int的最大值最小值
//int的最大值
System.out.println(Integer.MAX_VALUE);
//int的最小值
System.out.println(Integer.MIN_VALUE);
字符串与数值相互转换
数字–>字符串(第二种方式不介绍了)
String str = String.valueOf(5); //或者是String str = 5+"";
字符串–>数字
int i= Integer.parseInt("15");
String类
格式化输出
- %s 表示字符串
- %d 表示数字
- %n 表示换行
printf和format能够达到一模一样的效果,如何通过eclipse查看java源代码 可以看到,在printf中直接调用了format
String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
System.out.printf(sentenceFormat,name,kill,title);
字符与Character工具类
char用来保存一个字符,char对应的封装类是Character
public class TestChar {public static void main(String[] args) {System.out.println(Character.isLetter('a'));//判断是否为字母System.out.println(Character.isDigit('a')); //判断是否为数字System.out.println(Character.isWhitespace(' ')); //是否是空白System.out.println(Character.isUpperCase('a')); //是否是大写System.out.println(Character.isLowerCase('a')); //是否是小写System.out.println(Character.toUpperCase('a')); //转换为大写System.out.println(Character.toLowerCase('A')); //转换为小写String a = 'a'; //不能够直接把一个字符转换成字符串String a2 = Character.toString('a'); //转换为字符串}
}
字符串操作
charAt | 获取字符 |
---|---|
toCharArray | 获取对应的字符数组 |
subString | 截取子字符串 |
split | 分隔 |
trim | 去掉首尾空格 |
toLowerCase toUpperCase | 大小写 |
indexOf lastIndexOf contains | 定位 |
replaceAll replaceFirst | 替换 |
startsWith | 以…开始 |
endsWith | 以…结束 |
equals | 判断是否相等 |
equalsIgnoreCase | 忽略大小写判断是否相等 |
StringBuffer
package character;
public class TestString {public static void main(String[] args) {String str1 = "let there ";StringBuffer sb = new StringBuffer(str1); //根据str1创建一个StringBuffer对象sb.append("be light"); //在最后追加System.out.println(sb);sb.delete(4, 10);//删除4-10之间的字符,都是闭区间System.out.println(sb);sb.insert(4, "there ");//在4这个位置插入 thereSystem.out.println(sb);sb.reverse(); //反转System.out.println(sb);}
}
StringBuffer与StringBuilder的区别是:StringBuilder是线程安全的
Random类
方法 | 描述 |
---|---|
Random random = new Random(10); | 10是seed,可以不写 |
random.nextInt(10) | 随机在最小int~最大int取一个数 |
random.nextInt(10) | [0,x)的随机整数,x不能小于0 |
nextFloat() | 随机一个float |
nextDouble() | 随机一个Double |
nextLong() | 随机一个Long |
nextBoolean() | 随机true或false |
nextBytes(byte[] bytes) | 为一个byte类型的数组随机赋值 |
nextGaussian() | 随机生成一个高斯分布的浮点数 |
Math类
方法 | 描述 |
---|---|
Math.abs() | 取绝对值 |
Math.ceil() | 向上取整 |
Math.floor() | 向下取整 |
Math.round(float a) | 四舍五入取整 |
Math.max(int a,int b) | 去较大的值 |
Math.min(int a,int b) | 去较小的值 |
Math.pow(double a,double b) | a的b次幂 |
Math.random() | 返回一个[0.0,1.0)之间的double值 |
System类
方法 | 描述 |
---|---|
System.currentTimeMillis() | 获取系统当前毫秒值 |
System.exit(0); | 结束正在运行的Java程序 |
System.gc(); | 垃圾回收器 |
System.getProperties() | 确定当前的系统属性 |
System.arraycopy(src, 2, dest, 0, 2); | 复制数组 |
日期类
注意:是java.util.Date;
而非 java.sql.Date,此类是给数据库访问的时候使用的
时间原点概念
零这个数字,就代表Java中的时间原点,其对应的日期是1970年1月1日 8点0分0秒 ,
为什么对应1970年呢? 因为1969年发布了第一个 UNIX 版本:AT&T,综合考虑,当时就把1970年当做了时间原点。
所有的日期,都是以为这个0点为基准,每过一毫秒,就+1。
创建日期对象
// 当前时间
Date d1 = new Date();
System.out.println(d1);
// 从1970年1月1日 早上8点0分0秒 开始经历的毫秒数
Date d2 = new Date(5000);
System.out.println(d2);
Date().gettime()获取时间戳
返回long型的时间戳,直接打印对象,会看到 “Tue Jan 05 09:51:48 CST 2016” 这样的格式,可读性比较差,可以进行日期格式化
注:System.currentTimeMillis()
也可以达到gettime()的功能,而且更精确
日期的格式化
- y 代表年
- M 代表月
- d 代表日
- H 代表24进制的小时
- h 代表12进制的小时
- m 代表分钟
- s 代表秒
- S 代表毫秒
package date;import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDate {public static void main(String[] args) {SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );Date d= new Date();String str = sdf.format(d);System.out.println("当前时间通过 yyyy-MM-dd HH:mm:ss SSS 格式化后的输出: "+str);}
}
日历类
日历类可以转为日期类,还能翻日历,别鸡巴学了,感觉没啥用啊
import java.util.Calendar;
import java.util.Date;
public class TestDate {public static void main(String[] args) {//采用单例模式获取日历对象Calendar.getInstance();Calendar c = Calendar.getInstance();//通过日历对象得到日期对象Date d = c.getTime();Date d2 = new Date(0);c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00}
}
几个常见的关键字
static静态属性与静态方法
所有的饮用都能访问,用static修饰,到时候直接用 类+原点操作符
就能直接调用
例如Math库的Pi,math.pi
final(只能操作一次)
修饰类。这个类不能够被继承
修饰方法。这个方法不能够被重写
修饰基本类型变量。该变量只有一次赋值机会
修饰引用。表示该引用只有1次指向对象的机会
修饰常量。表示可以直接访问当不能修改的常量
public static final int itemTotalNumber = 6;
abstract(抽象)
在类中声明一个方法,这个方法没有实现体,是一个“空”方法 ,即抽象方法。
一个类有抽象方法,就必须声明为抽象类。一个抽象类可以没有抽象方法。
抽象类不能直接实例化,只能通过子类来实例化
一个子类继承了抽象类就必须重写抽象方法,重写的时候就不用写abstract了
2、lambda表达式
函数式编程思想
函数是有输入,有输出的额一个过程,面向对象思想强调的是一切必须通过对象的形式来做事情,
函数式思想则尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式去做,
lambda表达式就是函数式编程思想的体现
Lambda初体验
需求:启动一个线程,输出一句话
public class Test {public static void main(String[] args) {//实现类的方式//MyRunnable myRunnable = new MyRunnable();//Thread t = new Thread(myRunnable);//t.start()//匿名内部类的方式new Thread(new Runnable() {@Overridepublic void run() {System.out.println("多线程启动啦!");}}).start();//lambda方式改造new Thread( ()-> {System.out.println("多线程启动啦!");}).start();}
}
lambda方式分析:
- ()里面没内容,可以看作方法形参为空
- ->用箭头指向后面要做的事
- { }包含一段代码,称之为代码块,可以看成方法体中的内容
Lambda表达式标准格式
格式:(形参)->{代码块}
如果有多个形参就用逗号分开,没有为空即可
Lambda表达式使用前提
- 有一个接口
- 接口中
有且只有一个抽象方法
Lambda表达式练习
Addable是一个接口,其中有add这个方法,执行完控制台输出了30,
执行lambda表达式的时候,系统偷偷的自动创建了一个对象,lambda实现了add方法
lambda表达式的结果其实就是一个实现了接口的对象,这也解释了传的参数应该是Addable的实现对象
public class Test {public static void main(String[] args) {useAddable( (int x,int y)-> {return x+y;});}private static void useAddable(Addable a) {//传的肯定是Addable的实现对象,所以可以调用int sum = a.add(10,20);System.out.println(sum);}
}
省略模式
- 参数类型可以省略,但是不能只省略一个,要省略全省略
- 如果参数有且仅有一个,小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,如果有return也要把return省略
Lambda表达式与匿名内部类的区别
# 所需类型不同匿名内部类:可以是接口,也可以是抽象类,还可以是具体类Lambda表达式:只能是接口
# 使用限制不同如果接口中有且仅有一个抽象方法,可以使用lambda表达式,也可以使用匿名内部类如果接口有多个抽象方法,只能使用匿名内部类,不能使用lambda表达式
# 实现原理不同匿名内部类:编译以后产生一个单独的.class字节码文件Lambda表达式:编译之后没有一个单独的.class字节码文件,对应的字节码会在运行时动态生成
Lambda遍历Map
1.8之后可用
aMap.forEach( (k,v)->{System.out.println(k+" "+v);} );
3、异常处理
异常与错误的分类
Throwable类是Java语言中所有异常和错误的超类
- Error称为严重问题,不需要处理
- Excepption称为异常类,他表示程序本身可以处理的问题
- RuntimeException:在编译器检查不到,运行时出现问题
- 非RuntimeException:编译前就必须要进行处理,否则程序无法通过编译
JVM的默认处理方案
如果程序出现了问题,我们又没有做任何处理,最终JVM会做默认的处理:
- 将异常信息和出现异常的位置打印
- 程序停止执行
异常处理try…catch
嗐,千篇一律,无需多言
try {//todo
} catch (Exception e) {e.printStackTrace();//打印异常信息
} finally {//todo
}
Throwable的成员方法
Message是在构造方法中进行赋值的
方法 | 描述 |
---|---|
getMessage() | 返回此异常的原因 |
toString() | 返回该异常的原因和类名 |
printStackTrace()
|
将异常的类名,原因,出现异常代码的位置信息 |
编译时异常与运行时异常的区别
Java中异常分为两大类:编译时异常与运行时异常,也被称为受检异常和非受检异常
所有的RuntimeException类以及子类被称为运行时异常,其他的异常都是编译时异常
- 编译时异常:必须显示处理,否则程序就会发生错误,无法通过编译,如果使用throws,谁调用谁处理
- 运行时异常:无需显示处理,也可以和编译时异常一样处理
异常处理throws
并不是所有情况我们都有权限进行异常的处理,有时候可能出现的异常我们是处理不了的
针对这种情况,Java提供了throws解决方案,格式是跟在方法括号后面
:
public void yourMethod() throws 异常类名 {//todo
}
异常只是被抛出当前代码块了,但是在哪里使用还是需要进行try…catch的
自定义异常类
只要我们的类继承RuntimeException或者Exception就可以成为其中一员
编写一个异常类:
public class ScoreException extends Exception {public ScoreException() {}public ScoreException(String message) {super(message)}
}
使用这个异常:
public class GenericDemo {public void checkScore(int score) throws ScoreException {if (score<0 || score>100) {throw new ScoreException();//使用throw抛出这个异常} else {System.out.println("分数正常!");}}
}
throws和throw的区别
throws:
- 用在方法声明后,跟的是类名
- 表示抛出异常,由该方法调用者来处理
- 表示出现异常的一种可能,并不一定会发生这些异常
throw:
- 用在方法体内,跟的是异常对象名
- 表示抛出异常,由方法体内的语句进行处理
- 执行throw一定是抛出了某种异常
3、多线程
程序、进程、线程的区别
程序( program):是为完成特定任务、用某种语言编写的一组指令的集合是一段静态的代码
。(程序是静态的)
进程( process):是程序的一次执行过程。正在运行的一个程序,进程
作为资源分配的单位
,在内存中会为每个进程分配不同的内存区域。进程是动态的)是一个动的过程,进程的生命周期:有它自身的产生、存在和消亡的过程
线程( thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程的.
单核CPU与多核CPU
并发与并行的区别
并行:多个CPU同时执行多个任务
并发:一个CPU“同时”执行多个任务(采用时间片切换)
一个Java程序基本的三个线程
注:异常线程会影响主线程的执行
创建线程的三种方式
继承Thread类,重写run方法:
//继承Thread类
public class MyThread extends Thread {//重写run方法@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(i);}}public static void main(String[] args) {MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();//发现仍然是单线程,因为直接调用run方法其实并没有启动线程//myThread1.run();//myThread2.run();myThread1.start();myThread2.start();}
}
实现Runnable接口,实现run方法,将该类作为参数传入Thread对象:
public class MyRunnable implements Runnable {//重写run方法@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable,"猴子");Thread thread2 = new Thread(myRunnable,"大象");//使用匿名内部类的形式Thread thread3 = new Thread(){@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}};thread3.setName("老虎");thread1.start();thread2.start();thread3.start();}
}
实现Callable接口,jdk1.5之后出现:
前两种方式有两点不足:
- 没有返回值
- 不能抛出异常
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//Callable可以写成Callable<返回值类型>,不写则默认为Object
public class MyThread implements Callable {@Overridepublic Integer call() throws Exception {return new Random().nextInt(10);}public static void main(String[] args) throws ExecutionException, InterruptedException {MyThread myThread = new MyThread();FutureTask futureTask = new FutureTask(myThread);Thread thread = new Thread(futureTask);thread.start();//获取线程的返回值Object obj = futureTask.get();System.out.println(obj);}
}
线程生命周期
阻塞方式结束可以是join()
方法结束
加锁/加同步/同步监视器
方法一:利用同步代码块
即操作用synchronized包裹起来,就算synchronized包裹的是this,但是不影响多个线程对同一数据的同步,因为这里是实现的Runnable接口,创建线程的时候用的是同一个对象(即同一把锁),可以达到相同的目的。同时注意锁的时候不要锁多了,只需要锁一两步有安全隐患的代码就好了
synchronized (任意对象) {多条语句操作共享数据的代码
}
public class MyRunnable implements Runnable {private int number = 100;//重写run方法@Overridepublic void run() {while (true) {synchronized (this) {if (number>0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(number);number--;}}}}public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable, "窗口1");Thread thread2 = new Thread(myRunnable, "窗口2");thread1.start();thread2.start();}
}
继承Thread类出现synchronized不起作用的问题:
如果是继承Thread,同时synchronized(this)的话会失效,因为多个线程是不同的锁,不同的锁不会相互干扰,因此,没有达到同步的目的,可以写个字符串(字符串常量池 ),或者是一个static常量(最好也加上final修饰),或者是构造的时候穿过来个东西作为锁,最好写该类的字节码信息(MyClass.class)
,因为不管对象有多少个,类的字节码信息是唯一的。同时注意,当锁必须是引用数据类型,不能是基本数据类型
如果是final修饰了引用,只是不能修改地址而已,对象的属性还是可以修改的,修改了锁还是一个锁
如果两个方法使用同一个锁,当A方法把锁锁住后,B方法同样被锁住
方法二:使用同步方法
在方法前加上synchronized
修饰即可,但锁其实还是有问题,因为锁默认是类的this,如果是继承Thread类还是会有问题,怎么解决呢?可以加上static进行修饰,这样锁就是同一个了相当于类的字节码文件,不过就不能使用this了,可以通过Thread.currentThread().getname()获取线程名称,注意同步代码块效率更高
,同时,如果一个类中多个方法被synchronized
修饰,A方法调用锁住锁后B方法也会被锁住,即本类全锁,因为锁是this
public static synchronized void test(){//todo
}
利用lock锁(jdk1.5之后)
synchronized是Java中的关键字,这个关键字的识别是靠JVM来识别完成的呀,是虚拟机级别的。
而Lock锁是API级别的,提供了相应的接口和对应的实现类,这个方式更灵活,表现出来的性能优于之前的方式。
同时也注意是不是同一把锁的问题呀!!!,嫌麻烦就直接使用实现Runnable接口的方式就好了
Lock是接口,不能直接实例化,常采用它的实现类ReentrantLock
来实例化,代码如下:
public class MyRunnable implements Runnable {private int number = 100;private Lock lock = new ReentrantLock();//重写run方法@Overridepublic void run() {while (true) {try {lock.lock();if (number>0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(number);number--;}} finally {lock.unlock();}}}public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable, "窗口1");Thread thread2 = new Thread(myRunnable, "窗口2");thread1.start();thread2.start();}
}
注:通常情况下使用try...finally
来包裹操作,防止中间出现问题无法释放锁
原子访问
所谓的原子性操作
即不可中断
的操作,比如赋值操作,原子性操作本身是线程安全的
,但是i++
这个行为,事实上是有3个原子性操作组成的:
- 步骤 1. 取 i 的值
- 步骤 2. i + 1
- 步骤 3. 把新的值赋予i
JDK6 以后,新增加了一个包java.util.concurrent.atomic
,里面有各种原子类,比如AtomicIntege
而AtomicInteger
提供了各种自增,自减等方法,这些方法都是原子性的,同一个时间,只有一个线程可以调用这个方法。
public class TestThread {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicI =new AtomicInteger();int i = atomicI.decrementAndGet();int j = atomicI.incrementAndGet();int k = atomicI.addAndGet(3);}
}
线程安全类
- Hashtable不能存放null,HashMap可以存放 null
- Hashtable是线程安全的类,HashMap是非线程安全的
- StringBuffer 是线程安全的,StringBuilder 是非线程安全的,StringBuilder 更快且常用,因为不执行同步
- Vector是线程安全的类,而ArrayList是非线程安全的,ArrayList更快且常用,因为不执行同步
concurrent
包提供了一些高效的映射、有序集合、队列的实现:
ConcurrentHashMap
、ConcurrentSkipListMap
、ConcurrentSkipListSet
、ConcurrentLinkedQueue
借助Collections.synchronizedList
,可以把ArrayList转换为线程安全的List,与此类似的,还有HashSet,LinkedList,HashMap等等非线程安全的类
List<Integer> list2 = Collections.synchronizedList(list1);
线程通信——生产者消费者模式
- 生产者生产产品到仓库
- 如果仓库有东西,生产者不生产,等消费者消费
- 如果仓库没东西,等生产者生产
方法 | 说明 |
---|---|
wait() | 让当前线程等待,直到另一个线程调用notify()或notifyall() |
notify() | 唤醒正在等待的单个线程,沿着wait之后继续执行代码 |
notifyall() | 唤醒正在等待的所有线程,沿着wait之后继续执行代码 |
注:wait和notify必须放在synchronized修饰的方法或代码块中,wait释放了锁,sleep没有释放锁
奶箱:
public class Box {//表示剩下几瓶奶private int milk;//表示奶箱的状态private boolean state = false;//生产奶public synchronized void put(int milk) {if (state) {try {wait();//如果有牛奶应该是等待消费} catch (InterruptedException e) {e.printStackTrace();}}//没有牛奶再生产this.milk = milk;System.out.println("送奶工将第"+this.milk+"放入奶箱");state = true;//唤醒notify();}//消费奶public synchronized void get() {if (!state) {//没有牛奶就等try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果有牛奶就消费System.out.println("用户拿到第"+this.milk+"奶");state = false;//唤醒notify();}
}
生产者:
public class Productor implements Runnable {private Box b;public Productor(Box b) {this.b = b;}@Overridepublic void run() {for (int i = 1; i <= 5; i++) {b.put(i);}}
}
消费者:
public class Customer implements Runnable {private Box b;public Customer(Box b) {this.b = b;}@Overridepublic void run() {while (true) {b.get();}}
}
测试类:
public class BoxTest {public static void main(String[] args) {Box box = new Box();Productor productor = new Productor(box);Customer customer = new Customer(box);Thread thread1 = new Thread(productor);Thread thread2 = new Thread(customer);thread1.start();thread2.start();}
}
使用优化Lock锁线程通信
场景:一个生产者唤醒等待池中的线程,里面既有生产者也有消费者,一下子会全唤醒,这样不好,我们可以分开,即生产者有自己的等待池,消费者有自己的等待池,一个Lock可以拥有一个同步队列和多个等待队列
- Condition的await对应Object的wait
- Condition的signal对应Object的notify
- Condition的signalAll对应Object的notifyAll
class MyThread() {//xxxLock lock = new ReentrantLock();Condition produceCondition = lock.newCondition();//生产者等待队列Condition consumeCondition = lock.newCondition();//消费者等待队列//xxxpublic void setProduct() {lock.lock();try {if (xx) {//生产者进入生产者等待队列produceCondition.await();}//唤醒消费者来消费consumeCondition.signal();}finally{lock.unlock();}}
}
几个常用方法
启动一个线程
- start()方法:启动线程,然后由JVM调用此线程的run()方法
获取当前线程
Thread提供了currentThread()
静态方法获取当前线程
Thread.currentThread().setName("主线程");
线程的名字
- setName()
- getName()
super(name)
:通过构造方法往上传
线程优先级
Java实现的是抢占式调度模型,即优先级高的线程可以获得的CPU时间片段多一点。除此之外还有分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片,默认为5,最小为1,最大为10
- getPriority()获取线程优先级
- setPriority()设置线程优先级
线程优先执行——join方法
当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程,即将其他线程进入阻塞状态。
注意:必须先 start,再join才有效
人为制造阻塞——sleep方法
时间以毫秒为单位
Thread.sleep(1000);//线程阻塞一秒钟
设置守护线程——setDaemon(true)
将A线程设置为B线程的伴随线程,当B线程停止的时候,A线程也不要存在了,注意,要先将A设置为守护线程再启动A线程
线程被迫中止——stop()
该方法要被废弃了,可以了解一下
Thread.currentThread().stop();
线程池
每一个任务都去执行一个新的线程,开销还是很大,因此有了线程池,将任务丢到线程池中去执行即可
目前还用不到,等有需要会补充。
5、IO操作
File类常用操作
File是文件和目录的路径的抽象表示,它并不是一个真正的文件,仅仅是路径名,可以存在也可以不存在
方法 | 说明 |
---|---|
File(String pathname) | 直接写路径进行构造 |
File(String parent,String child) | 目录和子目录进行拼接,都是String |
File(File file,String child) | 前面是文件,后面是子目录的文件名 |
exists() | 判断文件是否存在 |
isDirectory() | 判断是否是文件夹 |
isFile() | 判断是否是文件(非文件夹) |
length() | 获取文件长度 |
lastModified() | 获取文件最后修改时间 |
setLastModified(0 )
|
设置文件修改时间为1970.1.1 08:00:00 |
f1.renameTo(f2) | 文件重命名 |
list() |
以字符串数组的形式 ,返回当前文件夹下的所有文件
|
listFiles() |
以文件数组的形式 ,返回当前文件夹下的所有文件
|
getParent() | 以字符串形式返回文件所在文件夹 |
getParentFile() | 以文件形式返回获取所在文件夹 |
mkdir() | 创建文件夹,如果父文件夹skin不存在,创建就无效 |
mkdirs() | 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹 |
createNewFile() | 创建一个空文件,如果父文件夹skin不存在,就会抛出异常 |
getParentFile().mkdirs() | 创建一个空文件之前,通常都会创建父目录 |
listRoots() | 列出所有的盘符c: d: e: 等等 |
delete() | 刪除文件 |
deleteOnExit(); | VM结束的时候,刪除文件,常用于临时文件的删除 |
递归遍历目录
public class Test {public static void main(String[] args) {//给定一个路径File srcFile = new File("E:\\IdeaProjects\\javatest");//调用方法getAllFilePath(srcFile);}public static void getAllFilePath(File srcFile) {File[] files = srcFile.listFiles();if (files != null) {for (File file : files) {if (file.isDirectory()) {//递归调用getAllFilePath(file);} else {System.out.println(file.getAbsolutePath());}}}}
}
IO流的概述与分类
IO:输入(读)/输出(写)(Input/Output),从其他位置加载到程序(内存)中称为输入,反之称为输出
流:流是一种抽象概念,是对数据传输的总称,数据在设备间的传输称为流,流的本质是数据传输
按数据的流向分:
- 输入流:读数据
- 输出流:写数据
按数据类型分:
- 字节流(万能):字节输入流、字节输出流
- 字符流:字符输入流、字符输出流
我们说的IO流的分类一般是按数据类型
分的
InputStream与OutputStream
InputStream是一个抽象类,是所有输入字节流类的超类
OutputStream也是一个抽象类,是所有字节输出流类的超类,输出流接收输出字符并将其发送到某个接收器
字节流写数据(字节输出流FileOutputStream)
如果文件不存在会自动创建,写数据的时候有三种方式
方法 | 说明 |
---|---|
write(int b) | 一次写一个字节 |
write(byte[] b) | 直接写一个字节数组 |
write(byte[] b,int off,int len) | 从off开始写,写的长度为len |
通常情况下,close方法放在finally
中,在close的时候先判断是否为null
public class Test {public static void main(String[] args) throws IOException {//给定一个路径,调用系统功能创建文件FileOutputStream fos = new FileOutputStream("E:\\IdeaProjects\\javatest\\fos.txt");//写数据,这里直接写97的时候是afos.write("97".getBytes());//关闭流并释放系统资源fos.close();}
}
字节流写数据如何换行以及追加
换行通过写入换行符即可,不同系统换行符不同:
- windows:\r\n
- Linux:\n
- mac:\r
追加写入的实现,在创建FileOutputStream的时候在后面添加true
,这样就是追加了
FileOutputStream fos = new FileOutputStream("E:\\IdeaProjects\\javatest\\fos.txt",true);
字节流读数据(字节输入流FileInputStream)
public class Test {public static void main(String[] args) throws IOException {//给定一个路径,调用系统功能创建文件FileInputStream fis = new FileInputStream("E:\\IdeaProjects\\javatest\\fos.txt");//读数据,一次读一个字节,如果文件达到末尾则返回-1int read = fis.read();System.out.println((char)read);//一次读完所有的内容int data;while ((data=fis.read())!=-1) {System.out.println((char) data);}//一次读取字节数组(常用)byte[] bytes = new byte[1024];int len;while ((len=fis.read(bytes))!=-1) {//这里经常写为fos.write(bytes);即写入输出流System.out.println(new String(bytes));}//关闭流并释放系统资源fis.close();}
}
★【经典文件复制案例】:使用字节数组
public class Test {public static void main(String[] args) throws IOException {//输入流读数据FileInputStream fis = new FileInputStream("E:\\IdeaProjects\\javatest\\fos.txt");//输出流写数据FileOutputStream fos = new FileOutputStream("E:\\IdeaProjects\\javatest\\fos-backup.txt");byte[] bytes = new byte[1024];int len;while ((len=fis.read(bytes))!=-1) {fos.write(bytes);}//关闭流并释放系统资源fos.close();fis.close();}
}
字节缓冲流
BufferedOutputStream
:类实现一个缓冲输出流。通过设置这样的输出流,一个应用程序可以写字节到基本的输出流,而不必导致每个字节写入的底层系统的调用。默认大小为8192字节BufferedInputStream
:当BufferedInputStream
创建,内部缓冲区创建数组。根据需要从流中读取字节或跳过时,内部缓冲区根据需要从包含的输入流中重新填充,一次许多字节。默认大小为8192字节
因为使用了缓冲流,大大减少了对底层的调用次数,因此可以提高效率。
注:字节缓冲流仅仅提供缓冲区
,而真正的读写数据还是要依靠基本的字节流对象
,因此构造方法中传入字节输入输出流,同时调用的方法与字节流对象相同
★【经典文件复制案例】:使用字节缓冲流
public class Test {public static void main(String[] args) throws IOException {//输入流读数据BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\IdeaProjects\\javatest\\fos.txt"));//输出流写数据BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\IdeaProjects\\javatest\\fos-backup.txt"));byte[] bytes = new byte[1024];int len;while ((len=bis.read(bytes))!=-1) {bos.write(bytes);}//关闭流并释放系统资源bos.close();bis.close();}
}
为什么会有字符流
字节流操作汉字或韩文、日文的时候不方便,因为读的时候是一个字节一个字节的读,而汉字在UTR-8
编码下占用3个字节
,在GBK
编码下占用2个字节
,因此直接输出会乱码,于是Java提供了字符流
字符流 = 字节流+编码表
注:汉字在存储的时候,不论使用哪种编码存储,第一个字节都是负数
字符编码解码的方法
public class Test {public static void main(String[] args) throws IOException {String s = "中国";//默认是UTF-8//[-28, -72, -83, -27, -101, -67]System.out.println(Arrays.toString(s.getBytes()));//[-28, -72, -83, -27, -101, -67]System.out.println(Arrays.toString(s.getBytes("UTF-8")));//[-42, -48, -71, -6]System.out.println(Arrays.toString(s.getBytes("GBK")));byte[] bytes = s.getBytes();//中国System.out.println(new String(bytes));//中国System.out.println(new String(bytes,"UTF-8"));//涓浗System.out.println(new String(bytes,"GBK"));}
}
Reader和Writer
Reader:字符输入流的抽象类
Writer:字符输出流的抽象类
字符流写数据(字符输出流OutputStreamWriter)
方法 | 说明 |
---|---|
write(int c) | 一次写一个字符 |
write(char[] c) | 直接写一个字节数组 |
write(char[] c,int off,int len) | 写一个字符数组的一部分,写的长度为len |
write(String str) |
写一个字符串(常用)
|
writer(String str,int off,int len) |
写一个字符串的一部分(常用)
|
public class Test {public static void main(String[] args) throws IOException {//第二个参数可以指定字符集OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\IdeaProjects\\javatest\\a.txt"));//刷新流,将数据进行操作,即从缓冲区释放osw.flush();osw.write("我爱你哦哦哦哦哦");osw.close();}
}
注:close首先会先刷新流,然后再关闭资源
字符流读数据(字符输入流OutputStreamReader)
public class Test {public static void main(String[] args) throws IOException {InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\IdeaProjects\\javatest\\a.txt"));//一次读一个字符int ch;while ((ch=isr.read())!=-1) {System.out.println((char)ch);}//使用字符数组char[] chars = new char[1024];int len;while ((len = isr.read(chars))!=-1) {System.out.print(new String(chars));}}
}
注:字符流与字节流读写数据的方式大同小异,只是对象不同
★【经典文件复制案例】字符流复制文件
public class Test {public static void main(String[] args) throws IOException {//字符输入流InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\IdeaProjects\\javatest\\a.txt"));//字符输出流OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\IdeaProjects\\javatest\\a-backup.txt"));//使用字符数组读写文件char[] chars = new char[1024];int len;while ((len = isr.read(chars))!=-1) {osw.write(chars);}osw.close();isr.close();}
}
★【经典文件复制案例——简化版】字符流复制文件
InputStreamReader与OutputStreamWriter操作起来太长了,可以使用子类FileReader
和FileWriter
,但是如果想解决编码问题还是要老老实实的使用父类
public class Test {public static void main(String[] args) throws IOException {//字符输入流FileReader isr = new FileReader("E:\\IdeaProjects\\javatest\\a.txt");//字符输出流FileWriter osw = new FileWriter("E:\\IdeaProjects\\javatest\\a-backup.txt");//使用字符数组char[] chars = new char[1024];int len;while ((len = isr.read(chars))!=-1) {osw.write(chars);}osw.close();isr.close();}
}
字符缓冲流
BufferedReader
与BufferedWriter
BufferedWriter
有特有的方法newLine()
,写入一个换行符,这个函数会调用系统默认的换行符
BufferedReader
有特有的方法readLine()
,读取一行文字,如果达到末尾则返回null
★【经典文件复制案例】使用字符缓冲流
public class Test {public static void main(String[] args) throws IOException {//读BufferedReader reader = new BufferedReader(new FileReader("E:\\IdeaProjects\\javatest\\a.txt"));//写BufferedWriter writer = new BufferedWriter(new FileWriter("E:\\IdeaProjects\\javatest\\a-backup.txt"));//使用字符数组char[] chars = new char[1024];int len;while ((len = reader.read(chars))!=-1) {writer.write(chars);}reader.close();writer.close();}
}
★【常用】使用字符缓冲流特有功能复制
public class Test {public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new FileReader("E:\\IdeaProjects\\javatest\\a.txt"));BufferedWriter writer = new BufferedWriter(new FileWriter("E:\\IdeaProjects\\javatest\\a-backup.txt"));//使用字符数组String line;while ((line = reader.readLine())!=null) {writer.write(line);writer.newLine();}reader.close();writer.close();}
}
IO总结
什么是输入与输出
输入简单来说就是读
,是针对程序或者内存来说的,就是从外界读取,可以是从文件中,或者是网络请求中
输出简单来说就是写
,是针对程序或者内存来说的,将已经读取到的或者输入的信息写入外部文件中
几个IO操作类
字节流:
类名 | 功能 |
---|---|
InputStream | 所有字节输入流的超类 |
OutputStream | 所有字节输出流的超类 |
FileInputStream | 常用的字节输入流 |
FileOutputStream | 常用的字节输出流 |
BufferedInputStream | 字节输入缓冲流 |
BufferedOutputStream | 字节输出缓冲流 |
字符流:
类名 | 功能 |
---|---|
Reader | 所有字符输入流的超类 |
Writer | 所有字符输出流的超类 |
InputStreamReader | 常用的字符输入流 |
OutputStreamWriter | 常用的字符输出流 |
FileReader | 常用的字符输入流——简化版 |
FileWriter | 常用的字符输出流——简化版 |
BufferedReader | 字符输入缓冲流 |
BufferedWriter | 字符输出缓冲流 |
使用字节/字符数组读写的套路
char[] chars = new char[1024];
int len;
while ((len = isr.read(chars))!=-1) {osw.write(chars);
}
特殊操作流
标准输入流:System.in
public class Test {public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));//Java封装了一个类实现键盘录入,方便我们调用//Scanner scanner = new Scanner(System.in);while (true) {System.out.print("请输入一个字符串:");String line = reader.readLine();System.out.println("你输入的是:"+line);}}
}
标准输出流:System.out
public class Test {public static void main(String[] args) throws IOException {PrintStream out = System.out;out.println("hello,这里换行");out.print("oh,不换行");out.print("oh,不换行");}
}
字节打印流:PrintStream
,继承OutputStream
,只负责输出数据,有自己独有的方法
public class Test {public static void main(String[] args) throws FileNotFoundException {PrintStream ps = new PrintStream("E:\\IdeaProjects\\javatest\\ps.txt");//使用字节输出流的形式写到文件,是aps.write(97);//使用特有的方法写入文件,看到的就是97ps.println(97);//ps.print(99);不换行}
}
字符打印流:PrintWriter
public class Test {public static void main(String[] args) throws FileNotFoundException {//如果后面加了true,会自动刷新缓冲区PrintWriter ps = new PrintWriter("E:\\IdeaProjects\\javatest\\ps.txt");//字符流的形式写到文件,是aps.write(97);ps.write("\r\n");ps.flush();//使用特有的方法写入文件,看到的就是97ps.println(97);ps.flush();}
}
对象序列化流:ObjectOutputStream
,需要对象实现Serializable
接口,但不需要重写任何方法
public class Student implements Serializable {private int age;private String name;public Student(int age, String name) {this.age = age;this.name = name;}public static void main(String[] args) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IdeaProjects\\javatest\\a.txt"));oos.writeObject(new Student(10,"小黑"));oos.close();}
}
对象反序列化流:ObjectInputStream
,可以反序列化ObjectOutputStream
序列化之后的对象,需要对象实现Serializable
接口
public class Student implements Serializable {private int age;private String name;public Student(int age, String name) {this.age = age;this.name = name;}public static void main(String[] args) throws IOException, ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\IdeaProjects\\javatest\\a.txt"));Object object = ois.readObject();Student student = (Student) object;System.out.println(student.name+":"+student.age);}
}
6、网络编程
什么是网络编程
在网络通信协议下,实现网络互联的不同计算机上运行的程序可以进行资源交换,简而言之,用户之间可以发送消息或文件
网络编程三要素
IP地址
IP地址是网络中设备的唯一标识,分为IPv4和IPv6两大类,我们使用IPv4进行网络通信,它采用点分十进制表示法,一般长这样192.168.99.1
,127.0.0.1
可以代表本机地址,一般用于测试
命令 | 说明 |
---|---|
ipconfig | 查看本机IP |
ping IP | 检查网络是否连通 |
端口
网络的通信本质上是两个应用程序的通信,每台计算机上有许多程序,每个程序都运行在某一个端口上,端口号可以作为应用程序的唯一标识,普通应用程序要使用1024之后的端口号
协议
在计算机网络中,连接和通信的规则称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换,常见的协议有TCP协议和UDP协议
TCP
协议是面向连接
的通信协议,可以为两台计算机提供可靠无差别
的数据传输,在TCP协议中必须明确客户端与服务器,由客户端向服务器发起连接请求,每次连接要经过三次挥手
UDP
协议是面向无连接
的通信协议,,不管对方有没有收到,反正只负责发出去,由于UDP耗费资源小,通信效率高,通常会用于传输音频、视频、和普通数据以及直播
关于三次握手,四次挥手的理解
三次握手以及四次挥手其实就是确保客户端和服务器知道彼此
都具备接收和发送
数据的能力,以及做好了分开的准备,其中,以三次挥手为例。
- 客户端发起连接请求,服务器知道客户端有发送数据的能力,但是不知道客户端有没有接收的能力,与此同时,客户端不知道服务器是否具备接收和发送数据的能力
- 服务器回送响应,客户端知道服务器有了接受和发送数据的能力
- 客户端发送确认信息,服务器知道客户端接收到了上次的响应,服务器知道客户端有了接收数据的能力
InetAddress的使用
InetAddress是Java提供的一个类,方便我们对IP地址进行获取和操作
public class MyInetAddress {public static void main(String[] args) throws UnknownHostException {//通过主机名或ip地址获取对象InetAddress address = InetAddress.getByName("192.168.99.1");//获取主机名String hostName = address.getHostName();System.out.println("主机名:"+hostName);//获取IP地址字符串String hostAddress = address.getHostAddress();System.out.println("IP地址:"+hostAddress);}
}
UDP发送数据
Java提供了DatagramSocket类基于UDPP协议的Socket
发送数据的步骤:
- 创建Socket对象
- 创建数据并将数据打包
- 调用DatagramSocket对象的方法发送数据
- 关闭发送端
public class TcpSendDemo {public static void main(String[] args) throws Exception {String hostName = "192.168.99.1";int port = 6666;//创建socket对象DatagramSocket socket = new DatagramSocket();//准备数据,控制台读取System.out.print("请输入你要发送的内容:");BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String text = reader.readLine();byte[] data = text.getBytes();//将数据打包DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(hostName),port);//发送数据socket.send(packet);socket.close();}
}
UDP接收数据
接收数据的步骤:
- 创建接收端的Socket对象
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台显示
- 关闭接收端
public class UdpReceiveDemo {public static void main(String[] args) throws IOException {//UDP接收只需要知道端口号就可以了int port = 6666;//创建socket对象DatagramSocket socket = new DatagramSocket(port);//创建一个数据包接收数据byte[] bytes = new byte[1024];DatagramPacket packet = new DatagramPacket(bytes, bytes.length);//调用方法接收数据,阻塞等待socket.receive(packet);//解析数据包byte[] data = packet.getData();int length = packet.getLength();//这里指定长度是消除后面的空字符,因为有时候不够1024String dataString = new String(data, 0, length);System.out.println("收到:"+dataString);//关闭socket.close();}
}
TCP发送数据
客户端和服务端会建立虚拟链路,是通过这个虚拟链路进行通信的。Java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
发送数据的步骤:
- 创建客户端的Socket对象
- 获取输出流,写数据
- 释放资源
public class TcpClientDemo {public static void main(String[] args) throws IOException {String hostName = "192.168.99.1";int port = 7777;//创建客户端对象Socket socket = new Socket(hostName, port);//获取输出流,写数据OutputStream outputStream = socket.getOutputStream();outputStream.write("你好啊,tcp".getBytes());//通知服务端,我传输完了socket.shutdownOutput();//得到服务器反馈InputStream inputStream = socket.getInputStream();byte[] bytes = new byte[1024];int len = inputStream.read(bytes);String dataString = new String(bytes, 0, len);System.out.println("接收到服务器:"+dataString);//释放资源socket.close();}
}
TCP接收数据
接收数据的步骤:
- 创建服务器的Socket对象ServerSocket
- 获取输入流,读数据,把数据显示
- 释放资源
public class TcpServerDemo {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket serverSocket = new ServerSocket(7777);//监听socket的连接Socket accept = serverSocket.accept();//获取输入流,读数据InputStream inputStream = accept.getInputStream();byte[] bytes = new byte[1024];//一种是读字节,另一种是读字节数组,文本东西少,读一遍就好了int len = inputStream.read(bytes);String dataString = new String(bytes, 0, len);System.out.println("接收客户端到:"+dataString);//返回信息OutputStream outputStream = accept.getOutputStream();outputStream.write("已成功接收!".getBytes());//关闭资源serverSocket.close();accept.close();}
}
TCP中socket的补充
服务器可以通过accept方法获取道连接的socket,这个socket可以获取输入流(客户端传到服务器的信息),然后将输入流写入字符数组。同时这个accept得到的socket对象也可以获取输出流,用同样的套路去给客户端写东西,客户端的socket使用socket.getInputStream()
方法,使用同样的套路去获取服务器的信息
BufferedWriter的应用
在写的时候可以使用封装了OutputStream的BufferBuilder,直接调用write方法,将字符流转为字节流,功能等同于输出流的write。将getOutputStream
字节输出流转化为了OutputStreamWriter
字符输出流,再采用BufferedWriter
写数据
public class TcpClientDemo {public static void main(String[] args) throws IOException {String hostName = "192.168.99.1";int port = 7777;//创建客户端对象Socket socket = new Socket(hostName, port);//字符输入流BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String text = reader.readLine();//获取输出流,写数据BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));String line;while ((line=reader.readLine())!=null) {bufferedWriter.write(line);bufferedWriter.newLine();bufferedWriter.flush();}//释放资源socket.close();}
}
BufferedReader的应用
将字节流直接转为字符流
public class TcpServerDemo {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket serverSocket = new ServerSocket(7777);//监听socket的连接Socket accept = serverSocket.accept();//获取输入流,读数据BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(accept.getInputStream()));String line;while ((line=bufferedReader.readLine())!=null) {System.out.println("接收客户端到:"+line);}serverSocket.close();accept.close();}
}
TCP接收文本文件
将FileWriter
封装为bufferedWriter
,客户端代码可以用上面的BufferedWriter的应用
的代码
public class TcpServerDemo {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket serverSocket = new ServerSocket(7777);//监听socket的连接Socket accept = serverSocket.accept();//获取输入流,读数据BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(accept.getInputStream()));//把数据写入文本文件BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("a.txt"));String line;while ((line=bufferedReader.readLine())!=null) {bufferedWriter.write(line);bufferedWriter.newLine();bufferedWriter.flush();}bufferedReader.close();bufferedWriter.close();serverSocket.close();accept.close();}
}
TCP上传文本文件
服务器的程序和上面TCP服务器内容写入文本文件
是一样的
public class TcpClientDemo {public static void main(String[] args) throws IOException {String hostName = "192.168.99.1";int port = 7777;//创建客户端对象Socket socket = new Socket(hostName, port);//字符输入流,读数据BufferedReader bufferedReader = new BufferedReader(new FileReader("b.txt"));//获取输出流,写数据BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));String line;while ((line=bufferedReader.readLine())!=null) {bufferedWriter.write(line);bufferedWriter.newLine();bufferedWriter.flush();}//释放资源bufferedReader.close();bufferedWriter.close();socket.close();}
}
多线程实现文件上传
服务器一直开着,每来一个客户端就开一个线程,实现思路如下:
创建一个实现了Runnable
接口的类,构造方法中的参数是Socket类型,文件上传操作放在run方法中,服务器使用while死循环,accept得到的对象传入线程,然后调用start方法
7、泛型
介绍
泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
它的本质是参数化类型
,也就是说所操作的数据类型被指定为一个参数
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
泛型定义格式:
- <类型>:指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时候给定的类型可以看成是实参,并且
实参的类型只能是引用数据类型
泛型的优点
先看这段代码,这样就报错了!
public class Test {public static void main(String[] args) {ArrayList<Object> objectArrayList = new ArrayList<>();objectArrayList.add("hello");objectArrayList.add("world");objectArrayList.add(666);Iterator<Object> iterator = objectArrayList.iterator();while (iterator.hasNext()) {//报错java.lang.ClassCastException: //java.lang.Integer cannot be cast to java.lang.StringString next = (String) iterator.next();System.out.println(next);}}
}
ArrayList
使用了泛型,我们可以指定具体类型的数据
public class Test {public static void main(String[] args) {ArrayList<String> objectArrayList = new ArrayList<>();objectArrayList.add("hello");objectArrayList.add("world");//objectArrayList.add(666);不能写,将运行时期的异常提前到了编译期Iterator<Object> iterator = objectArrayList.iterator();while (iterator.hasNext()) {String next = iterator.next();//不用强制转换System.out.println(next);}}
}
使用泛型的好处:
- 将运行时期的异常提前到了编译期
- 避免了类型强制转换(因为指定了固定的类型,所以只能放一种类型)
泛型类(创建对象的时候明确类型)
泛型类的定义格式:T可以写成任意字母,一般使用T、E、K、V
public class Generic<T> {//
}
泛型类
public class Generic<T> {private T name;public void setName(T name) {this.name = name;}public T getName() {return name;}
}
泛型测试类
public class GenericTest {public static void main(String[] args) {Test<String> test = new Test<>();//指定T为String类型test.setName("小黑龙");System.out.println(test.getName());}
}
泛型方法(调用方法的时候明确类型)
先看看这段代码
public class Test {public void show(String s) {System.out.println(s);}public void show(Integer s) {System.out.println(s);}public void show(Boolean s) {System.out.println(s);}
}
使用泛型类进行改善
public class Test<T> {public void show(T t) {System.out.println(t);}//但是使用的时候每次都需要去规定T的类型public static void main(String[] args) {Test<String> stringTest = new Test<>();stringTest.show("11111");Test<Integer> integerTest = new Test<>();integerTest.show(11111);Test<Boolean> booleanTest = new Test<>();booleanTest.show(true);}
}
泛型方法格式:帅到爆炸!!!酷毙了
public class Test {public <T> void show(T t) {System.out.println(t);}public static void main(String[] args) {Test test = new Test();test.show("1111");test.show(1111);test.show(true);}
}
泛型接口
泛型接口:
public interface Generic<T> {void show(T t);
}
泛型实现类:
public class GenericImpl<T> implements Generic<T> {@Overridepublic void show(T t) {System.out.println(t);}
}
测试类:
public class Test {public static void main(String[] args) {GenericImpl<String> generic1 = new GenericImpl<>();generic1.show("11111");GenericImpl<Integer> generic2 = new GenericImpl<>();generic2.show(11111);}
}
类型通配符<?>
List<?>可以表示元素类型未知的List,他的元素可以是任何类型
<?>
是类型通配符类型通配符的上限:
<? extends 类型>
,泛型的类型(等号后面那部分)只能是该类型或该类型的子类类型通配符的上限:
<? super 类型>
,泛型的类型只能是该类型或该类型的父类
public class GenericDemo {public static void main(String[] args) {//类型通配符<?>List<?> list1 = new ArrayList<Object>();List<?> list2 = new ArrayList<Number>();List<?> list3 = new ArrayList<Integer>();//类型通配符上限<? extends 类型>
// List<? extends Number> list4 = new ArrayList<Object>();//报错List<? extends Number> list5 = new ArrayList<Number>();List<? extends Number> list6 = new ArrayList<Integer>();//类型通配符下限<? super 类型>List<? super Number> list7 = new ArrayList<Object>();List<? super Number> list8 = new ArrayList<Number>();
// List<? super Number> list9 = new ArrayList<Integer>();//报错}
}
8、数据结构与集合框架
Java的api文档:https://www.runoob.com/manual/jdk11api/java.base/java/util/package-summary.html
数组
数组声明
int[] arr2 = new int[5];//推荐这种
int arr[] = new int[5];
数组初始化
int arr[] = new int[]{1, 3, 5, 7, 9};
int[] arr2 = {2, 4, 6, 8, 10};
添加元素以及取出元素
int[] arr = new int[5];
arr[0] = 1;
int a = arr[0];
遍历数组
public static void main(String[] args) {int arr[] = new int[]{1, 3, 5, 7 ,9};int[] arr2 = {2, 4, 6, 8, 10};for (int i = 0; i < arr.length; ++i) {System.out.print(arr[i] + "\t"); // 1 3 5 7 9}for (int x: arr2) {System.out.print(x + "\t"); // 2 4 6 8 10}
}
Arrays工具类的常用操作
方法 | 功能 | 备注 |
---|---|---|
fill(int[] a, int val) | 填充数组 | |
fill(int[] a, int fromIndex, int toIndex, int val) | 填充指定索引区间数组 | 左闭右开 |
sort(int[] a) | 数组排序 | |
sort(int[] a, int fromIndex, int toIndex) | 排序指定索引的元素 | |
copyOf(int[] original, int newLength) | 复制数组 | 指定新数组长度 |
copyOfRange(int[] original, int from, int to) | 复制数组 | 指定所复制的原数组的索引 |
Arrays.asList(stringArray).contains(“a”); | 检查数组中是否包含某一个值 | |
Arrays.binarySearch(str) | 定位元素位置 | 前提是有序数组有序数组 |
Arrays.asList.indexOf(str); | 定位元素位置 |
ArrayUtils工具类的常用操作
方法 | 功能 | 备注 |
---|---|---|
ArrayUtils.addAll(intArray, intArray2); | 连接两个数组 | |
ArrayUtils.reverse(intArray); | 数组翻转 | |
ArrayUtils.removeElement(intArray, 3) | 从数组中移除一个元素 | 返回一个删除后的新数组 |
字符串
String是不可变类型,被final修饰,即赋值后不能被修改
字符串的格式化
String fs = String.format("浮点型变量的值为%f," +"整型变量的值为%d," +"字符串变量的值为%s", floatVar, intVar, stringVar);
String类常用操作
方法 | 功能 | 备注 |
---|---|---|
charAt(int index) | 返回指定索引处的 char 值。 | |
contains() | 判断是否包含指定的字符 | |
endsWith(String suffix) | 测试此字符串是否以指定的后缀结束。 | |
equals(Object anObject) | 将此字符串与指定的对象比较。 | |
equalsIgnoreCase(String anotherString) | 两个String 比较,不考虑大小写。 | |
indexOf(String str) | 返回子串在此字符串中第一次出现处的索引。 | |
lastIndexOf(int ch) | 返回字符最后一次出现处的索引。 | |
int length() | 返回此字符串的长度。 | |
replace(char oldChar, char newChar) | 替换子串 | 返回一个新的字符串 |
split(String regex) | 字符串分割 | |
substring(int beginIndex,int endIndex) | 字符串切片 | 返回一个新的字符串 |
toLowerCase() | 使用默认语言环境的规则将此 String 中的所有字符都转换为小写。 | |
toUpperCase() | 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。 | |
trim() | 删除字符串左右空元素 | 返回字符串的副本 |
isEmpty() | 判断字符串是否为空。 |
集合框架
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。
Java集合框架图
Java集合框架体系图
ArrayList
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。
初始化
import java.util.ArrayList; // 引入 ArrayList 类
ArrayList<E> objectName =new ArrayList<>(); // 初始化
ArrayList类常用操作
方法 | 功能 | 备注 |
---|---|---|
add() |
将元素插入到指定位置的 arraylist 中
|
|
addAll() | 添加集合中的所有元素到 arraylist 中 | |
clear() | 删除 arraylist 中的所有元素 | |
clone() | 复制一份 arraylist | |
contains() |
判断元素是否在 arraylist
|
|
get() |
通过索引值获取 arraylist 中的元素
|
|
indexOf() |
返回 arraylist 中元素的索引值
|
|
removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 | |
remove() |
删除 arraylist 里的单个元素
|
|
size() | 返回 arraylist 里元素数量 | |
isEmpty() | 判断 arraylist 是否为空 | |
subList() | 截取部分 arraylist 的元素 | 左开右闭 |
set(index,newValue) |
更新指定索引的元素
|
|
sort() |
对 arraylist 元素进行排序
|
默认为升序排列 |
toArray() | 将 arraylist 转换为数组 | |
lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 | |
retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 | |
containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 | |
forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
LinkedList
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表可分为单向链表和双向链表。
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
LinkedList常用操作
方法 | 描述 | 备注 |
---|---|---|
add(E e)
|
末尾添加元素 | 返回布尔值 |
add(int index, E element) | 向指定位置插入元素。 | 返回布尔值 |
addFirst(E e)
|
元素添加到头部。 | |
addLast(E e)
|
元素添加到尾部。 | |
get(int index) | 返回指定位置的元素。 | |
getFirst()
|
返回第一个元素。 | |
getLast()
|
返回最后一个元素。 | |
remove() | 删除并返回第一个元素。 | |
removeFirst()
|
删除并返回第一个元素。 | |
removeLast()
|
删除并返回最后一个元素。 | |
remove(Object o) | 删除某一元素 | 返回布尔值 |
remove(int index) | 删除指定位置的元素。 | |
set(int index, E element)
|
设置指定位置的元素。 | |
contains(Object o)
|
判断是否含有某一元素。 | |
indexOf(Object o)
|
查找指定元素从前往后第一次出现的索引。 | |
lastIndexOf(Object o) | 查找指定元素最后一次出现的索引。 | |
clear() | 清空链表。 | |
size() | 返回链表元素个数。 | |
addAll(Collection c) | 将一个集合的所有元素添加到链表后面 | 返回布尔值 |
addAll(int index, Collection c) | 将一个集合的所有元素添加到链表的指定位置后面 | 返回布尔值 |
ArrayList与LinkedList的抉择
ArrayList :是顺序结构,查找和修改速度快,就像电影票
LinkedList :是链表结构,增加和删除速度快,就像佛珠
HashSet
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合
HashSet 允许有 null 值
HashSet 是无序的,即不会记录插入的顺序
HashSet 不是线程安全的
HashSet 实现了 Set 接口
HashSet类常用方法
方法 | 描述 |
---|---|
add() | 添加元素 |
remove(value) | 删除元素 |
contoins(value) | 判断是否存在元素 |
size() | 得到元素个数 |
for-each | 迭代 |
HashSet、LinkedHashSet、TreeSet的抉择
HashSet: 无序
LinkedHashSet: 按照插入顺序
TreeSet: 从小到大排序
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
HashMap类常用操作
方法 | 功能 |
---|---|
put() | 将键/值对添加到 hashMap 中 |
get() | 获取指定 key 对应对 value |
getOrDefault() | 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
remove() | 删除指定键 key 的映射关系 |
containsKey() | 是否存在指定的 key |
keySet | 返回 hashMap 中所有 key 组成的集合视图。 |
values() | 返回 hashMap 中存在的所有 value 值。 |
isEmpty() | 判断 hashMap 是否为空 |
size() | 计算 hashMap 中键/值对的数量 |
forEach() | 对 hashMap 中的每个映射执行指定的操作。 |
containsValue() | 检查 hashMap 中是否存在指定的 value 对应的映射关系。 |
HashMap与Hashtable的抉择
HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
- HashMap可以存放 null
- Hashtable不能存放null
区别2:
- HashMap不是线程安全的类
- Hashtable是线程安全的类
Collections工具类
Collections是一个类,由静态方法组成,是针对集合框架,容器的工具类,就如同Arrays是数组的工具类
方法 | 功能 |
---|---|
reverse() | 反转 |
shuffle() | 随机打乱 |
sort() | 升序排序 |
swap() | 交换 |
synchronizedList() | 线程安全化 |
自然排序的使用
retuen的结果会有什么影响:
- return 负数----表示后面的比前面的小,后来的元素放在前面
- return 0----表示两个是同一个元素
- return 正数----表示后面的比前面的大,后来的元素放在后面
自定义排序的口诀:
升序this在前----降序this在后
方法:类去实现Comparable
接口,重写compareTo
方法
public class Student implements Comparable<Student> {int uid;int score;public Student(int uid, int score) {this.uid = uid;this.score = score;}@Overridepublic int compareTo(Student student) {// 升序this在前----降序this在后return this.score-student.score;}public static void main(String[] args) {TreeSet<Student> studentHashSet = new TreeSet<>();studentHashSet.add(new Student(1,11));studentHashSet.add(new Student(5,6));studentHashSet.add(new Student(7,13));studentHashSet.add(new Student(2,17));studentHashSet.add(new Student(6,9));for (Student student : studentHashSet) {System.out.println(student.uid+":"+student.score);}}
}
自定义排序方法:比较器排序Comparator
使用comparator接口对ArrayList排序无效
public class Student {int uid;int score;public Student(int uid, int score) {this.uid = uid;this.score = score;}public static void main(String[] args) {TreeSet<Student> studentHashSet = new TreeSet<Student>(new Comparator<Student>() {@Overridepublic int compare(Student student1,Student student2) {//升序前减后,降序后减前return student1.score-student2.score;}});studentHashSet.add(new Student(1,11));studentHashSet.add(new Student(5,6));studentHashSet.add(new Student(7,13));studentHashSet.add(new Student(2,17));studentHashSet.add(new Student(6,9));for (Student student : studentHashSet) {System.out.println(student.uid+":"+student.score);}}
}
Comparable与Comparator的对比
Comparable是通过放置的对象去实现Comparable接口的compareTo
方法,升序this在前,降序this在后
Comparator是通过创建的集合去实现匿名内部类,升序前减后,降序后减前
哈希值与哈希表
哈希值是JDK根据对象的地址
或者字符串
或者数字
算出来的int类型的数值,可以通过对象的hashCode()
方法获取,默认情况下不同对象的哈希值是不同的,重写hashCode()方法后可以让不同对象拥有相同哈希值
9、JDBC
JDBC简介
JDBC:Java Database Connectivity–>Java和数据库的连接技术
是Sun公司推出的程序访问数据库的规范(接口),各个数据库厂商实现接口,提供数据驱动jar包,我们可以通过这套接口编程,但是真正执行的是驱动jar包中的类
环境准备
maven项目可以直接导入依赖:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version>
</dependency>
普通工程的步骤:
先下载MySQL的jar包点击下载
将jar包复制到项目的lib文件夹下(随便一个就行)
右键
lib
文件夹,点击Add as Library
快速入门
- 导入jar包(MySQL5版本之后的jar包可以省略这一步)
- 注册驱动
- 获取数据库连接对象 Connection
- 获取执行sql语句的对象 Statement
- 定义sql
- 执行sql,接受返回结果
- 处理结果并释放资源
public class JDBCTest {public static void main(String[] args) throws Exception {//注册驱动,MySQL5版本之后这句话可以不写了Class.forName("mysql.mysql.jdbc.Driver");//获取连接对象String url = "jdbc:mysql://loaclhost:3306/dbname";String username = "root";String password = "123456";Connection conn = DriverManager.getConnection(url,username,password);//获取执行sql的对象,statementStatement statement = conn.createStatement();//定义sql,别忘了加分号String sql = "update account set password=11111 where username=zhangsan;";//执行sqlint count = statement.executeUpdate(sql);//处理结果System.out.println(count);//释放资源statement.close();conn.close();}
}
详解各个对象
DriverManager
驱动管理对象
- registerDriver()方法注册驱动,加载时静态代码块自动调用(MySQL5版本之后不用写了)
- getConnection()方法获取数据库连接,url、username、password
如果是本地的loaclhost还是3306端口号,这个url可以简化为jdbc:mysql:///dbname
Connection
数据库连接对象
- 获取执行sql的对象
Statement createStatement()
,执行静态sqlStatement prepareStatement(String sql)
,执行动态sql
- 管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
,设置参数为false即开启事务 - 提交事务:
commit()
- 回滚事务:
rollback()
- 开启事务:
Statement
执行sql的对象
boolean execute(String sql)
:可以执行任意sql,但是用的不多int executeUpdate(String sql)
:执行DML(insert、update、delete)语句和DDL(库、表)语句- 返回值是受影响的行数,如果小于零则执行失败
ResultSet executeQuery(String sql)
:指定DQL(select)语句
ResultSet
结果集对象,封装查询的结果
- next():光标向下移动一行,有则获取下一行,没有则返回false
- getxxx(yyy):获取数据,xxx是数据类型,yyy是int时第几个(从1开始),yyy是String时是找某一列
ResultSet resultSet = statement.executeQuery(sql);
//和迭代器类似
while (resultSet.next()) {int anInt = resultSet.getInt(1);String id = resultSet.getString("id");
}
PreparedStatement
执行sql的对象,有时候使用字符串拼接会因为字段的特殊符号导致sql注入
用?
表示,作为占位符即可
String sql = "update account set password=? where username=?;";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setString(0,"111");//表示第一个占位符
statement.setObject(1,2222);
statement.executeUpdate();
JDBC工具类
import java.sql.*;public class JDBCUtils {static Connection conn = null;static String url;static String username;static String password;//初始化static {//加载类try {Class.forName("mysql.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}//获取连接对象try {conn = DriverManager.getConnection(url,username,password);} catch (SQLException e) {e.printStackTrace();}}public static Connection getConnection() throws Exception {return conn;};public static void close() {}public static void close(ResultSet resultSet, Connection conn, Statement statement) throws Exception {if (resultSet!=null) {conn.close();}if (conn!=null) {conn.close();}if (statement!=null) {statement.close();}}
}
使用事务
我这个例子不是很合适了,因为只有一条sql,但是演示了使用事务的三个步骤
Connection conn = DriverManager.getConnection(url,username,password);
PreparedStatement statement = null;
//获取执行sql的对象,statement
try {conn.setAutoCommit(false);statement = conn.prepareStatement("update account set password=? where username=?;");statement.setString(1,"xiaozhng");statement.setString(1,"zhang");//提交conn.commit();
}catch (Exception e) {//有问题回滚conn.rollback();
} finally {//释放资源statement.close();conn.close();
}
使用批处理
如果使用循环来做效率会很低
Connection conn = DriverManager.getConnection(url,username,password);
conn.setAutoCommit(false);
PreparedStatement statement = conn.prepareStatement("update account set password=? where username=?;");for (int i = 0; i < 50000; i++) {statement.setString(1,"xiaozhng");statement.setString(1,"zhang");statement.addBatch();//添加批处理if (i%1000==0) {statement.executeBatch();statement.clearBatch();}
}
二进制类型数据的读写
写入:
Connection conn = DriverManager.getConnection(url,username,password);
conn.setAutoCommit(false);
PreparedStatement statement = conn.prepareStatement("update user set photo=? where id=1;");
statement.setBlob(1,new FileInputStream("path"));
statement.executeUpdate();
读取:
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {InputStream inputStream = resultSet.getBinaryStream("photo");FileOutputStream fos = new FileOutputStream("path");int len;byte[] bytes = new byte[1024];while ((len=inputStream.read(bytes))!=-1) {fos.write(bytes,0,len);}
}
DBUtils的使用
是apache的开源框架,简化了JDBC的开发步骤,使得我们可以用更少量的代码实现连接数据库的功能,最厉害的是返回值可以直接指定,单个值,列表,Bean,BeanList…,maven页
- query可以增删查数据
- update可以修改数据
ResultSetHandler结果集处理类:
- ArrayHandler 将结果集中的第一条记录封装到一个Object[]数组中:new ArrayHandler()
- ArrayListHandler 将结果集中的每一条记录都封装到一个Object[]数组中:new ArrayListHandler()
- BeanHandler 将结果集中第一条记录封装到一个指定的类中:new BeanHandler(Sort.class)
- BeanListHandler 将结果集中每一条记录封装到指定的自定义类中,将这些类在封装到List集合中:new BeanListHandler<自定义类>(类名.class)
- ColumnListHandler 将结果集中指定的列的字段值,封装到一个List集合中:new ColumnListHandler(“字段名”)
- ScalarHandler 它是用于单数据。例如select count(*) from 表操作。:new ScalarHandler()
- MapHandler 将结果集第一行封装到Map<String,Object>集合中,Key 列名, Value 该列数据
- MapListHandler 将结果集每一行封装到List<Map<String,Object>>集合中,Key 列名, Value 该列数据,Map集合存储到List集合
public class DBUtilsTest {public static void main(String[] args) throws Exception {Connection connection = JDBCUtils.getConnection();QueryRunner qr = new QueryRunner();//查询单个值Object query = qr.query(connection, "select count(*) from user;", new ScalarHandler<>());//查询一个Bean,BeanListHandler<>()是Bean列表Map query1 = qr.query(connection,"select * from admin where id=?", new BeanHandler<>(Map.class), 1);//修改数据int result = qr.update(connection,"update from user set password=? where id=?","123");}
}
druid(德鲁伊)连接池技术
用户每次来不会向系统申请,而是去连接池中使用访问对象,用完再还回来,能节约时间还提高利用率
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/test_go?characterEncoding=UTF-8&useSSL=falseusername: xxxpassword: xxxxxxdriver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 5max-active: 20test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 30000filters: statasync-init: true
public ResultResponse testDruid() {String sql = "SELECT mobile FROM user WHERE id = ?";String mobile = jdbcTemplate.queryForObject(sql, new Object[]{1}, String.class);return new ResultResponse(201, "hey" + mobile);
}
唉就到这吧,,,等后面直接学Spring Boot整合得了
10、swing图形界面
模板
public class TestGUI {public static void main(String[] args) {// 主窗体JFrame f = new JFrame("LoL");// 主窗体设置大小f.setSize(400, 300);// 主窗体设置位置f.setLocation(200, 200);// 主窗体中的组件设置为绝对定位,否则为自动填充f.setLayout(null);// 按钮组件JButton b = new JButton("一键秒对方基地挂");// 同时设置组件的大小和位置b.setBounds(50, 50, 280, 30);// 把按钮加入到主窗体中f.add(b);// 关闭窗体的时候,退出程序f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 让窗体变得可见f.setVisible(true);}
}
监听事件
按钮绑定事件(按钮监听)
创建一个匿名类实现ActionListener接口,当按钮被点击时,actionPerformed方法就会被调用
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(580, 200);f.setLayout(null);final JLabel l = new JLabel();ImageIcon i = new ImageIcon("C:\Users\Administrator\Desktop\shana.png");l.setIcon(i);l.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());JButton b = new JButton("隐藏图片");b.setBounds(150, 200, 100, 30);// 给按钮 增加 监听b.addActionListener(new ActionListener() {// 当按钮被点击时,就会触发 ActionEvent事件// actionPerformed 方法就会被执行public void actionPerformed(ActionEvent e) {l.setVisible(false);}});f.add(l);f.add(b);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
键盘监听
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(580, 200);f.setLayout(null);final JLabel l = new JLabel();ImageIcon i = new ImageIcon("C:\\Users\\Administrator\\Desktop\\shana.png");l.setIcon(i);l.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());// 增加键盘监听f.addKeyListener(new KeyListener() {// 键被弹起public void keyReleased(KeyEvent e) {System.out.println(e.getKeyCode());//查看按键编码// 39代表按下了 “右键”if (e.getKeyCode() == 39) {// 图片向右移动 (y坐标不变,x坐标增加)l.setLocation(l.getX() + 10, l.getY());}}//键被按下public void keyPressed(KeyEvent e) {// TODO Auto-generated method stub}// 一个按下弹起的组合动作public void keyTyped(KeyEvent e) {}});f.add(l);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
鼠标监听
写的代码很多,常用适配器
MouseListener 鼠标监听器
mouseReleased 鼠标释放
mousePressed 鼠标按下
mouseExited 鼠标退出
mouseEntered 鼠标进入
mouseClicked 鼠标点击
在本例中,使用mouseEntered,当鼠标进入图片的时候,图片就移动位置
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;public class TestGUI {public static void main(String[] args) {final JFrame f = new JFrame("LoL");f.setSize(800, 600);f.setLocationRelativeTo(null);f.setLayout(null);final JLabel l = new JLabel();ImageIcon i = new ImageIcon("C:\\Users\\Administrator\\Desktop\\shana.png");l.setIcon(i);l.setBounds(375, 275, i.getIconWidth(), i.getIconHeight());f.add(l);l.addMouseListener(new MouseListener() {// 释放鼠标public void mouseReleased(MouseEvent e) {// TODO Auto-generated method stub}// 按下鼠标public void mousePressed(MouseEvent e) {// TODO Auto-generated method stub}// 鼠标退出public void mouseExited(MouseEvent e) {// TODO Auto-generated method stub}// 鼠标进入public void mouseEntered(MouseEvent e) {Random r = new Random();int x = r.nextInt(f.getWidth() - l.getWidth());int y = r.nextInt(f.getHeight() - l.getHeight());l.setLocation(x, y);}// 按下释放组合动作为点击鼠标public void mouseClicked(MouseEvent e) {// TODO Auto-generated method stub}});f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
适配器(常用)
MouseAdapter 鼠标监听适配器
一般说来在写监听器的时候,会实现MouseListener。
但是MouseListener里面有很多方法实际上都没有用到,比如mouseReleased ,mousePressed,mouseExited等等。
这个时候就可以使用 鼠标监听适配器,MouseAdapter 只需要重写必要的方法即可。
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;public class TestGUI {public static void main(String[] args) {final JFrame f = new JFrame("LoL");f.setSize(800, 600);f.setLocationRelativeTo(null);f.setLayout(null);final JLabel l = new JLabel("");ImageIcon i = new ImageIcon("C:\\Users\\Administrator\\Desktop\\shana.png");l.setIcon(i);l.setBounds(375, 275, i.getIconWidth(), i.getIconHeight());f.add(l);// MouseAdapter 适配器,只需要重写用到的方法,没有用到的就不用写了l.addMouseListener(new MouseAdapter() {// 只有mouseEntered用到了public void mouseEntered(MouseEvent e) {Random r = new Random();int x = r.nextInt(f.getWidth() - l.getWidth());int y = r.nextInt(f.getHeight() - l.getHeight());l.setLocation(x, y);}});f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
窗口类别
1、java的图形界面中,容器是用来存放 按钮,输入框等组件的。
窗体型容器有两个,一个是JFrame,一个是JDialog
2、JFrame是最常用的窗体型容器,默认情况下,在右上角有最大化最小化按钮
JDialog也是窗体型容器,右上角没有最大和最小化按钮
3、通过调用方法 setResizable(false); 做到窗体大小不可变化
4、模态窗口:当一个对话框被设置为模态的时候,其背后的父窗体,是不能被激活的,除非该对话框被关闭
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("外部窗体");f.setSize(800, 600);f.setLocation(100, 100);// 根据外部窗体实例化JDialogJDialog d = new JDialog(f);// 设置为模态d.setModal(true);d.setTitle("模态的对话框");d.setSize(400, 300);d.setLocation(200, 200);d.setLayout(null);JButton b = new JButton("一键秒对方基地挂");b.setBounds(50, 50, 280, 30);d.add(b);f.setVisible(true);d.setVisible(true);}
}
布局器
绝对定位(本人常用)
import javax.swing.JButton;
import javax.swing.JFrame;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);// 设置布局器为null,即进行绝对定位,容器上的组件都需要指定位置和大小f.setLayout(null);JButton b1 = new JButton("英雄1");// 指定位置和大小b1.setBounds(50, 50, 80, 30);JButton b2 = new JButton("英雄2");b2.setBounds(150, 50, 80, 30);JButton b3 = new JButton("英雄3");b3.setBounds(250, 50, 80, 30);// 没有指定位置和大小,不会出现在容器上JButton b4 = new JButton("英雄3");f.add(b1);f.add(b2);f.add(b3);// b4没有指定位置和大小,不会出现在容器上f.add(b4);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
FlowLayout顺序布局器
设置布局器为FlowLayout,顺序布局器
容器上的组件水平摆放
加入到容器即可,无需单独指定大小和位置
import java.awt.FlowLayout;import javax.swing.JButton;
import javax.swing.JFrame;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);// 设置布局器为FlowLayerout// 容器上的组件水平摆放f.setLayout(new FlowLayout());JButton b1 = new JButton("英雄1");JButton b2 = new JButton("英雄2");JButton b3 = new JButton("英雄3");// 加入到容器即可,无需单独指定大小和位置f.add(b1);f.add(b2);f.add(b3);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
BorderLayout
容器上的组件按照上北 下南 左西 右东 中的顺序摆放
import java.awt.BorderLayout;
import java.awt.FlowLayout;import javax.swing.JButton;
import javax.swing.JFrame;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);// 设置布局器为BorderLayerout// 容器上的组件按照上北下南左西右东中的顺序摆放f.setLayout(new BorderLayout());JButton b1 = new JButton("洪七");JButton b2 = new JButton("段智兴");JButton b3 = new JButton("欧阳锋");JButton b4 = new JButton("黄药师");JButton b5 = new JButton("周伯通");// 加入到容器的时候,需要指定位置f.add(b1, BorderLayout.NORTH);f.add(b2, BorderLayout.SOUTH);f.add(b3, BorderLayout.WEST);f.add(b4, BorderLayout.EAST);f.add(b5, BorderLayout.CENTER);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
GridLayout(网格布局器)
import java.awt.GridLayout;import javax.swing.JButton;
import javax.swing.JFrame;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);// 设置布局器为GridLayerout,即网格布局器// 该GridLayerout的构造方法表示该网格是2行3列f.setLayout(new GridLayout(2, 3));JButton b1 = new JButton("洪七");JButton b2 = new JButton("段智兴");JButton b3 = new JButton("欧阳锋");JButton b4 = new JButton("黄药师");JButton b5 = new JButton("周伯通");f.add(b1);f.add(b2);f.add(b3);f.add(b4);f.add(b5);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
setPreferredSize
即便 使用 布局器 ,也可以 通过setPreferredSize,向布局器建议该组件显示的大小.
注 只对部分布局器起作用,比如FlowLayout可以起作用。 比如GridLayout就不起作用,因为网格布局器必须对齐
import java.awt.Dimension;
import java.awt.FlowLayout;import javax.swing.JButton;
import javax.swing.JFrame;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);f.setLayout(new FlowLayout());JButton b1 = new JButton("英雄1");JButton b2 = new JButton("英雄2");JButton b3 = new JButton("英雄3");// 即便 使用 布局器 ,也可以 通过setPreferredSize,向布局器建议该组件显示的大小b3.setPreferredSize(new Dimension(180, 40));f.add(b1);f.add(b2);f.add(b3);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
CardLayout
因为CardLayout需要用到面板,JComboBox这些内容暂时还没学的内容,所以放在后面讲: CardLayout
常用组件
关键字 | 简介 |
---|---|
JLabel | 标签 |
setIcon | 使用JLabel显示图片 |
JButton | 按钮 |
JCheckBox | 复选框 |
JRadioButton | 单选框 |
ButtonGroup | 按钮组 |
JComboBox | 下拉框 |
JOptionPane | 对话框 |
JTextField | 文本框 |
JPasswordField | 密码框 |
JTextArea | 文本域 |
JProgressBar | 进度条 |
JFileChooser | 文件选择器 |
与python类似,可以绑定事件,代码用到了再去查
面板
基本面板(JPanel)
JPanel即为基本面板
面板和JFrame一样都是容器,不过面板一般用来充当中间容器,把组件放在面板上,然后再把面板放在窗体上。
一旦移动一个面板,其上面的组件,就会全部统一跟着移动,采用这种方式,便于进行整体界面的设计
ContentPane
JFrame上有一层面板,叫做ContentPane
平时通过f.add()向JFrame增加组件,其实是向JFrame上的 ContentPane加东西
ContentPane这个毛毯其实又放在了其他的毛毯上面
分隔条(SplitPanel)
创建一个水平JSplitPane,左边是pLeft,右边是pRight
import java.awt.Color;
import java.awt.FlowLayout;import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);f.setLayout(null);JPanel pLeft = new JPanel();pLeft.setBounds(50, 50, 300, 60);pLeft.setBackground(Color.RED);pLeft.setLayout(new FlowLayout());JButton b1 = new JButton("盖伦");JButton b2 = new JButton("提莫");JButton b3 = new JButton("安妮");pLeft.add(b1);pLeft.add(b2);pLeft.add(b3);JPanel pRight = new JPanel();JButton b4 = new JButton("英雄4");JButton b5 = new JButton("英雄5");JButton b6 = new JButton("英雄6");pRight.add(b4);pRight.add(b5);pRight.add(b6);pRight.setBackground(Color.BLUE);pRight.setBounds(10, 150, 300, 60);// 创建一个水平JSplitPane,左边是p1,右边是p2JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, pLeft, pRight);// 设置分割条的位置sp.setDividerLocation(80);// 把sp当作ContentPanef.setContentPane(sp);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
滚动条(JScrollPanel)
使用带滚动条的面板有两种方式
在创建JScrollPane,把组件作为参数传进去
JScrollPane sp = new JScrollPane(ta);
希望带滚动条的面板显示其他组件的时候,调用setViewportView
sp.setViewportView(ta);
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);f.setLayout(null);//准备一个文本域,在里面放很多数据JTextArea ta = new JTextArea();for (int i = 0; i < 1000; i++) {ta.append(String.valueOf(i));}//自动换行ta.setLineWrap(true);JScrollPane sp = new JScrollPane(ta);f.setContentPane(sp);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
多个小面板(TabbedPanel)
import java.awt.Color;
import java.awt.FlowLayout;import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);f.setLayout(null);JPanel p1 = new JPanel();p1.setBounds(50, 50, 300, 60);p1.setBackground(Color.RED);p1.setLayout(new FlowLayout());JButton b1 = new JButton("英雄1");JButton b2 = new JButton("英雄2");JButton b3 = new JButton("英雄3");p1.add(b1);p1.add(b2);p1.add(b3);JPanel p2 = new JPanel();JButton b4 = new JButton("英雄4");JButton b5 = new JButton("英雄5");JButton b6 = new JButton("英雄6");p2.add(b4);p2.add(b5);p2.add(b6);p2.setBackground(Color.BLUE);p2.setBounds(10, 150, 300, 60);JTabbedPane tp = new JTabbedPane();tp.add(p1);tp.add(p2);// 设置tab的标题tp.setTitleAt(0, "红色tab");tp.setTitleAt(1, "蓝色tab");ImageIcon i = new ImageIcon("e:/project/j2se/j.png");tp.setIconAt(0,i );tp.setIconAt(1,i );f.setContentPane(tp);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
布局器(CardLayerout)
CardLayerout 布局器 很像TabbedPanel ,在本例里面上面是一个下拉框,下面是一个CardLayerout 的JPanel
这个JPanel里有两个面板,可以通过CardLayerout方便的切换
使用菜单(JMenu)
先建立JmenuBar(菜单栏),然后添加菜单JMenu,把组件放到JMenu,然后把JmenuBar放上去
把菜单栏加入到frame,这里用的是set而非add f.setJMenuBar(mb);
在菜单栏放菜单项,还可以分割的
package gui;import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 400);f.setLocation(200, 200);JMenuBar mb = new JMenuBar();JMenu mHero = new JMenu("英雄");JMenu mItem = new JMenu("道具");JMenu mWord = new JMenu("符文");JMenu mSummon = new JMenu("召唤师");JMenu mTalent = new JMenu("天赋树");// 菜单项mHero.add(new JMenuItem("近战-Warriar"));mHero.add(new JMenuItem("远程-Range"));mHero.add(new JMenuItem("物理-physical"));mHero.add(new JMenuItem("坦克-Tank"));mHero.add(new JMenuItem("法系-Mage"));mHero.add(new JMenuItem("辅助-Support"));mHero.add(new JMenuItem("打野-Jungle"));mHero.add(new JMenuItem("突进-Charge"));mHero.add(new JMenuItem("男性-Boy"));mHero.add(new JMenuItem("女性-Girl"));// 分隔符mHero.addSeparator();mHero.add(new JMenuItem("所有-All"));mb.add(mHero);mb.add(mItem);mb.add(mWord);mb.add(mSummon);mb.add(mTalent);f.setJMenuBar(mb);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}
}
工具栏(JToolBar)
表格控件(JTable、TableModel)
日期控件
swing没有自带的日期控件,需要第三方的类
Swing的线程
有三种线程
1、初始化线程
初始化线程用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。
2、事件调度线程
通过事件监听的学习,我们了解到Swing是一个事件驱动的模型,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。比如点击一个按钮,对应的ActionListener.actionPerformed 方法中的代码,就是在事件调度线程 Event Dispatch Thread中执行的。
3、长耗时任务线程
有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的 长耗时任务线程中进行
Swing的皮肤
风格和皮肤可以自由切换的,还可以设置成windows风格,在实例化的部分添加这段代码即可切换成windows风格
try {UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");this.socket = new Socket(config.getServerIp(), config.getServerPort());this.self = self;
} catch (Exception e) {e.printStackTrace();
}
11、注解与反射
什么是注解
注解与反射是所有框架的底层,例如Mybatis、Spring等等,注解(Annotation)是给程序看的,注释(Comment)是给人看的,注解是Java基础中最简单的一章,不用感觉很难。
注解的作用
注解由JDK5.0引入,它不是程序本身,但是可以对程序做出解释,可以被其他程序(例如:编译器)读取,注解不是必须的,但有时候因为程序需要所以要写
注解的格式
以@注解名
的形式存在,也可以添加一些参数值,例如:@SuppressWarnings(value="unchecked")
注解在哪里使用
内置注解
@Override重写
@Deprecated方法废弃
@SuppressWarnings(value=“all”)镇压警告信息
元注解
- @Target表示注解可以用在什么地方
- @Retention表示需要在什么级别保存该注释信息,用于描述注解生命周期,SOURCE<CLASS<RUNTIME(常用)
- @Documented说明该注解将被包含在javadoc中
- @Inherited说明子类可以继承父类中的注解
import java.lang.annotation.*;//测试元注解
public class Test {@MyAnnotationpublic void test() {}
}//定义一个注解,这里METHOD是放在方法上的注解,TYPE可以放在类上
//@Target(value = ElementType.METHOD)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//注解运行时有效
@Retention(value = RetentionPolicy.RUNTIME)
//将注解生成在javadoc中
@Documented
//子类可以继承父类的注解
@Inherited
@interface MyAnnotation{}
自定义注解
使用@interface
自定义注解的时候,自动继承了java.lang.annotation.Annotation
接口
- @interface用来声明一个注解,格式: public @interface 注解名{定义内容}
- 其中的每一个方法实际上是声明了—个配置参数
- 方法的名称就是参数的名称.
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为va|ue
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
import java.lang.annotation.*;public class Test {//注解可以显示赋值,如果没有默认值则必须赋值@MyAnnotation(name = "hello")public void test() {}
}@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation{//注解的参数:参数类型+参数名+()String name() default "";int age() default 0;int id() default -1;//如果默认值为-1则表示不存在,和indexof异曲同工
}
反射机制
反射让java变成了动态语言
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言: Object-C、C#、 JavaScript、PHP、 Python等
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活
什么是反射
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection ap取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c= Class.forName(java.lang.String");
加载完类之后,在堆內存的方法区中就产生了一个Class类型的对象(一个类只有一个Cass对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。反射会引起类的主动引用,即类似new
了个对象
优点:
可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于
直接执行相同的操作,比new出来慢了几十倍。
反射提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- 等等
反射初体验
public class Test {public static void main(String[] args) throws ClassNotFoundException {Class c1 = Class.forName("User");//类的包路径System.out.println(c1);//一个类在内存中只有一个class对象//一个类被加载后,类的整个结构都会被封装在Class对象中Class c2 = Class.forName("User");Class c3 = Class.forName("User");Class c4 = Class.forName("User");System.out.println(c2.hashCode());System.out.println(c3.hashCode());System.out.println(c4.hashCode());}}class User{String name;int age;int id;
}
Class类
在Object类中定义了以下方法:
public final Class getClass()
以上的方法返回值的类型是一个C|ass类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
常用的方法:
获取Class的几种方式
public class Test {public static void main(String[] args) throws ClassNotFoundException {Person person = new Student();System.out.println("这个人是"+person.name);//方式一获得Class c1 = person.getClass();System.out.println(c1.hashCode());//方式二Class c2 = Class.forName("Student");System.out.println(c2.hashCode());//方式三Class c3 = Student.class;System.out.println(c3.hashCode());//方式四,基本内置类型的包装类都有一个TYPE属性Class c4 = Integer.TYPE;System.out.println(c4.hashCode());//获得父类类型Class c5 = c1.getSuperclass();String name = c5.getName();System.out.println(c5);//以后还可以用ClassLoader}
}
class Person{public String name;
}
class Student extends Person{Student() {this.name = "学生";}
}
哪些类型可以有Class对象
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@ interface
- primitive type:基本数据类型
- void
主动引用与被动引用
主动引用
java类的初始化阶段,虚拟机规范严格规定了5种情况必须立即对类进行初始化。
- 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发初始化。
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类时,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。
- 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_geStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
被动引用
除了上述5种场景,其他所有类的方式都不会触发初始化,称为被动引用。例如:
- 子类调用父类的静态方法,不会实例化子类
- 通过数组定义类引用,不会触发类的初始化
- 通过调用常量不会触发此类的初始化,因为常量在链接阶段已经存入调用类的常量池中了
反射获取泛型信息
反射获取注解信息
import java.lang.annotation.*;
import java.lang.reflect.Field;public class Test {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class<?> c1 = Class.forName("User");//反射获取注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println(annotation);}//获得注解的value值MyTable table = c1.getAnnotation(MyTable.class);//首先获取指定注解System.out.println(table.value());//获得类指定的注解Field f = c1.getDeclaredField("name");MyField annotation = f.getAnnotation(MyField.class);System.out.println(annotation.columnName());System.out.println(annotation.type());System.out.println(annotation.length());}
}@MyTable("db_User")
class User{@MyField(columnName = "db_user",type = "varchar",length = 12)public String name;@MyField(columnName = "db_user",type = "int",length = 10)public int age;
}@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTable{String value();
}@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField{String columnName();String type();int length();
}
12、方法引用
13、函数式接口
JavaSE基础知识汇总相关推荐
- 脑科学与脑电基础知识汇总
点击上面"脑机接口社区"关注我们 更多技术干货第一时间送达 脑科学与脑电基础知识汇总 该部分汇总了社区分享的部分脑科学.EEG.fNIRS.BCI.人机交互等相关知识. 脑电与情绪 ...
- python基础知识资料-Python基础知识汇总
原标题:Python基础知识汇总 1.Anaconda的安装 百度Anaconda的官网,下载左边的Python3.X版本 然后是设置路径,最后给出Jupyter notebook.具体参考: 猴子: ...
- python基础知识资料-学习Python列表的基础知识汇总
千里之行,始于足下.要练成一双洞悉一切的眼睛,还是得先把基本功扎扎实实地学好.今天,本喵带大家仔细温习一下Python的列表.温故而知新,不亦说乎. 当然,温习的同时也要发散思考,因为有些看似无关紧要 ...
- 计算机网络把许多什么连接在一起,计算机网络技术基础知识汇总习题
计算机网络技术基础知识汇总习题 1.21世纪要实现信息化,就必须依靠完善的网络,这里的网络是指,向用户提供不同服务的 电信网络,有线电视网络和计算机网络三种网络.(电信网络,有线电视网络,计算机网络) ...
- 计算机公共基础知识论文,计算机等级考试二级公共基础知识汇总.doc
计算机等级考试二级公共基础知识汇总.doc 计算机等级考试二级公共基础知识 第1章 数据结构与算法 1.1 算法 1.1.1 算法的基本概念 算法是指对解题方案的准确而完整的描述.简单地说,就是解决问 ...
- 网络基础知识汇总学习
一.网线(双绞线)连接线的制作 双绞线制作有 568A 和 568B 两个标准,日常以 568B 标准较常用. 568B 标准按颜色排序为: 1- 橙白. 2- 橙.3- 绿白.4- 蓝. 5- 蓝白 ...
- Serdes基础知识汇总
Serdes基础知识汇总 从知乎https://zhuanlan.zhihu.com/p/423321485转载 在开始了解高速接口的时候,必然会涉及到SerDes.serdes的知识点实际上非常多, ...
- S7-200SMART PLC基础知识汇总
S7-200SMART PLC基础知识汇总 输入输出: I:数字量输入(DI) Q:数字量输出(DO) AI:模拟量输入 AQ:模拟量输出 内部数据存储区: V:变量存储区,可以按位.字节.字或双字来 ...
- 4.电子计算机的分类,公基计算机基础知识汇总40
公基计算机基础知识汇总40 (3页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 3. 运算精度高电子计算机具有以往计算机无法比拟的计算精度, ...
最新文章
- Qt动态库静态库的创建、使用、多级库依赖、动态库改成静态库等详细说明
- MySQL · 引擎特性 · InnoDB 崩溃恢复过程
- Vue项目中一些常见的文件名及作用
- sklearn PCA特征降维
- 5、mysql中的库操作
- 前端学习(3020):vue+element今日头条管理--创建路由和配置路由
- 【AI视野·今日NLP 自然语言处理论文速览 第十一期】Mon, 21 Jun 2021
- STM32F103:二.(6)mrc522卡号读取
- 实用机器人设计(二)-传感器
- jQuery自定义右键菜单
- 我的世界服务器无限矿区块指令,我的世界区块刷新指令 | 手游网游页游攻略大全...
- 绿盟WEB服务扫描漏洞处理
- hightopo实现电力拓扑着色功能
- 冰点还原精灵怎么卸载
- 窗宽窗位与其处理方法
- 大数据多租户的概念_大数据平台 多租户
- c语言—冒泡排序(详解)
- JavaScript学习笔记之入门篇
- [论文阅读]Auto-Encoder Guided GAN for ChineseCalligraphy Synthesis
- torch从零开始搭建deeplabv3+训练自己的数据集!
热门文章
- 三步破解IntelliJ IDEA2017
- mybitplus name or service not known或quartz couldn‘t get host name
- java 实现文字转语音功能并同时生成语音文件 demo
- 用go写一个docker(9)-初步构造容器
- 电商运营基本常识你都知道哪些?
- Linux下安装与配置aMule电驴
- 一眼就看懂;Android App 开发前景介绍及学习路线规划
- Flutter Image从网络加载图片刷新、强制重新渲染
- 临床试验中lm是什么职位_除了CRX职位,临床试验中还有这些岗位
- 越狱苹果手机导出网易云音乐歌曲(以及缓存文件转换)