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,提供对应的getAbbsetAbb方法

类的继承

子类继承父类可以获得父类的属性和方法,一个父类可以被多个子类继承,但是一个子类只能继承一个父类,不过一个类可以实现多个接口,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包提供了一些高效的映射、有序集合、队列的实现:

ConcurrentHashMapConcurrentSkipListMapConcurrentSkipListSetConcurrentLinkedQueue

借助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操作起来太长了,可以使用子类FileReaderFileWriter,但是如果想解决编码问题还是要老老实实的使用父类

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();}
}

字符缓冲流

BufferedReaderBufferedWriter

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.1127.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(),执行静态sql
    • Statement 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基础知识汇总相关推荐

  1. 脑科学与脑电基础知识汇总

    点击上面"脑机接口社区"关注我们 更多技术干货第一时间送达 脑科学与脑电基础知识汇总 该部分汇总了社区分享的部分脑科学.EEG.fNIRS.BCI.人机交互等相关知识. 脑电与情绪 ...

  2. python基础知识资料-Python基础知识汇总

    原标题:Python基础知识汇总 1.Anaconda的安装 百度Anaconda的官网,下载左边的Python3.X版本 然后是设置路径,最后给出Jupyter notebook.具体参考: 猴子: ...

  3. python基础知识资料-学习Python列表的基础知识汇总

    千里之行,始于足下.要练成一双洞悉一切的眼睛,还是得先把基本功扎扎实实地学好.今天,本喵带大家仔细温习一下Python的列表.温故而知新,不亦说乎. 当然,温习的同时也要发散思考,因为有些看似无关紧要 ...

  4. 计算机网络把许多什么连接在一起,计算机网络技术基础知识汇总习题

    计算机网络技术基础知识汇总习题 1.21世纪要实现信息化,就必须依靠完善的网络,这里的网络是指,向用户提供不同服务的 电信网络,有线电视网络和计算机网络三种网络.(电信网络,有线电视网络,计算机网络) ...

  5. 计算机公共基础知识论文,计算机等级考试二级公共基础知识汇总.doc

    计算机等级考试二级公共基础知识汇总.doc 计算机等级考试二级公共基础知识 第1章 数据结构与算法 1.1 算法 1.1.1 算法的基本概念 算法是指对解题方案的准确而完整的描述.简单地说,就是解决问 ...

  6. 网络基础知识汇总学习

    一.网线(双绞线)连接线的制作 双绞线制作有 568A 和 568B 两个标准,日常以 568B 标准较常用. 568B 标准按颜色排序为: 1- 橙白. 2- 橙.3- 绿白.4- 蓝. 5- 蓝白 ...

  7. Serdes基础知识汇总

    Serdes基础知识汇总 从知乎https://zhuanlan.zhihu.com/p/423321485转载 在开始了解高速接口的时候,必然会涉及到SerDes.serdes的知识点实际上非常多, ...

  8. S7-200SMART PLC基础知识汇总

    S7-200SMART PLC基础知识汇总 输入输出: I:数字量输入(DI) Q:数字量输出(DO) AI:模拟量输入 AQ:模拟量输出 内部数据存储区: V:变量存储区,可以按位.字节.字或双字来 ...

  9. 4.电子计算机的分类,公基计算机基础知识汇总40

    公基计算机基础知识汇总40 (3页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 3. 运算精度高电子计算机具有以往计算机无法比拟的计算精度, ...

最新文章

  1. Qt动态库静态库的创建、使用、多级库依赖、动态库改成静态库等详细说明
  2. MySQL · 引擎特性 · InnoDB 崩溃恢复过程
  3. Vue项目中一些常见的文件名及作用
  4. sklearn PCA特征降维
  5. 5、mysql中的库操作
  6. 前端学习(3020):vue+element今日头条管理--创建路由和配置路由
  7. 【AI视野·今日NLP 自然语言处理论文速览 第十一期】Mon, 21 Jun 2021
  8. STM32F103:二.(6)mrc522卡号读取
  9. 实用机器人设计(二)-传感器
  10. jQuery自定义右键菜单
  11. 我的世界服务器无限矿区块指令,我的世界区块刷新指令 | 手游网游页游攻略大全...
  12. 绿盟WEB服务扫描漏洞处理
  13. hightopo实现电力拓扑着色功能
  14. 冰点还原精灵怎么卸载
  15. 窗宽窗位与其处理方法
  16. 大数据多租户的概念_大数据平台 多租户
  17. c语言—冒泡排序(详解)
  18. JavaScript学习笔记之入门篇
  19. [论文阅读]Auto-Encoder Guided GAN for ChineseCalligraphy Synthesis
  20. torch从零开始搭建deeplabv3+训练自己的数据集!

热门文章

  1. 三步破解IntelliJ IDEA2017
  2. mybitplus name or service not known或quartz couldn‘t get host name
  3. java 实现文字转语音功能并同时生成语音文件 demo
  4. 用go写一个docker(9)-初步构造容器
  5. 电商运营基本常识你都知道哪些?
  6. Linux下安装与配置aMule电驴
  7. 一眼就看懂;Android App 开发前景介绍及学习路线规划
  8. Flutter Image从网络加载图片刷新、强制重新渲染
  9. 临床试验中lm是什么职位_除了CRX职位,临床试验中还有这些岗位
  10. 越狱苹果手机导出网易云音乐歌曲(以及缓存文件转换)