赞
踩
包含的内容有: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主数据类型+引用=变量
类型 | 位数 | 值域 |
---|---|---|
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当做一个遥控器,远程遥控其他的行为。
当实例化一个对象的时候,会创建一个遥控器,遥控器在栈区,对象在堆区。
Book b=new Book();//new Book();只是创建一个对象,前面的b表示引用指向他,等号表示指向
Book c=new Book();
Book d=c;//这时候c和d两个遥控器遥控一个对象
c=b;//这时候c和b遥控同一个对象,但是d仍然遥控自己的那个对象
运算符 | 描述 |
---|---|
&& | 与 |
|| | 或 |
!= | 不等于 |
! | 非 |
短运算符
比如&&,当两端都为真才得到真,当左边为假便不会再计算右边
长运算符
比如&,强制虚拟机必须做两端的操作
符号 | 描述 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
++ | 自增 |
– | 自减 |
符号 | 描述 |
---|---|
> | 大于 |
>= | 大于或等于 |
< | 小于 |
<= | 小于或等于 |
== | 是否相等 |
!= | 不等于 |
参见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)
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]; //有两个一维数组,每个一维数组的长度是3
a[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}};
}
格式:可以通过values.for
快捷键实现
for (int each : values) {
System.out.println(each);//do something
}
包: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();
}
}
指向当前类本身的元素,类似c++,即使是同名的,this.name=name;
也可以赋值,称为隐式调用。
但是只要和属性不同名,直接赋值也OK的,this还能实现构造方法
的相互调用
要访问别的class用import导入哦,import package.class
标黄了就表示不能访问到,不写默认为protected
自身 | 同包子类 | 不同包子类 | 同包类 | 其他类 | |
---|---|---|---|---|---|
private | 访问 | 继承 | 继承 | 访问 | 访问 |
protected | 访问 | 继承 | 继承 | 访问 | 访问 |
public | 访问 | 继承 | 继承 | 访问 | 访问 |
package | 访问 | 继承 | 继承 | 访问 | 访问 |
对象属性初始化有3种
"some hero"
;封装是面向对象三大特征之一(封装、继承、多态),是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作与访问,成员变量设置为private
,提供对应的getAbb
和setAbb
方法
子类继承父类可以获得父类的属性和方法,一个父类可以被多个子类继承,但是一个子类只能继承一个父类,不过一个类可以实现多个接口,Object类是所有类的父类,类的继承关键字是extents
在子类方法中寻找一个变量时如下顺序:
子类中的所有构造方法都会默认先访问父类的无参构造方法
,子类会继承父类的数据,因此在子类初始化前一定要先进行父类初始化,每一个子类第一句话默认都是super()
顺序如下:先当前再长远
实例化子类的时候,会先调用父类的构造方法,当多个构造方法时,默认调用父类无参构造方法
如果想调用父类带参构造,需要用super,例如
super(name);//在ADHero带参构造下写,这个相当于调用参数为name的父类构造方法
super.speed;//父类的属性
super.useItem();//父类的方法
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引用是完全没问题的
父类转子类,向下:需要用到子类的类名进行强制转化,为什么要加呢,因为不知道子类的引用指向的对象是谁,
注:父类转子类之后再转子类可能会报错
只对方法进行声明,而不去实现,在java中,一个没有方法体的方法称为抽象方法,如果类中有抽象方法,该类必须定义为抽象类
abstruct
进行修饰该抽象类的子类对象
该抽象类的子类对象
接口就像一种公共规范,只要符合规范标准,大家就可以通用。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关系,表示这个对象能做什么,接口是更加纯粹的抽象。
注:最大的相同点是抽象类和接口都不能直接实例化
该接口的实现类对象
该接口的实现类对象
例如我们想升级接口,但是如果升级的话所有的实现类都需要实现这个新方法,可以使用一个新接口继承这个接口,然后找类去实现它,但是太麻烦了,于是有了默认实现方法
public interface Test{
public default void show() {//public可以省略,但是default不行
System.out.println("默认方法执行了,冲!");
}
}
静态方法只能被接口调用,这样做的目的是防止实现多个接口的时候,这多个接口存在同一个静态方法,导致混乱
public interface Test{
public static void show() {
System.out.println("静态方法执行了,冲!");
}
}
public class TestInterface{
public void main(String[] args) {
Test.show();
}
}
Java 9中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔:Java8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法
,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java9增加私有方法的必然性
定义的格式:
private 返回值类型 方法名(参数列表)
private static 返回值类型 方法名(参数列表)
内部类就是在类中定义一个类,例如在类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的最大值
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");
printf和format能够达到一模一样的效果,如何通过eclipse查看java源代码 可以看到,在printf中直接调用了format
String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
System.out.printf(sentenceFormat,name,kill,title);
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 | 忽略大小写判断是否相等 |
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这个位置插入 there System.out.println(sb); sb.reverse(); //反转 System.out.println(sb); } }
StringBuffer与StringBuilder的区别是:StringBuilder是线程安全的
方法 | 描述 |
---|---|
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.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.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);
返回long型的时间戳,直接打印对象,会看到 “Tue Jan 05 09:51:48 CST 2016” 这样的格式,可读性比较差,可以进行日期格式化
注:System.currentTimeMillis()
也可以达到gettime()的功能,而且更精确
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修饰,到时候直接用 类+原点操作符
就能直接调用
例如Math库的Pi,math.pi
修饰类。这个类不能够被继承
修饰方法。这个方法不能够被重写
修饰基本类型变量。该变量只有一次赋值机会
修饰引用。表示该引用只有1次指向对象的机会
修饰常量。表示可以直接访问当不能修改的常量
public static final int itemTotalNumber = 6;
在类中声明一个方法,这个方法没有实现体,是一个“空”方法 ,即抽象方法。
一个类有抽象方法,就必须声明为抽象类。一个抽象类可以没有抽象方法。
抽象类不能直接实例化,只能通过子类来实例化
一个子类继承了抽象类就必须重写抽象方法,重写的时候就不用写abstract了
函数是有输入,有输出的额一个过程,面向对象思想强调的是一切必须通过对象的形式来做事情,
函数式思想则尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式去做,
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() { @Override public void run() { System.out.println("多线程启动啦!"); } }).start(); //lambda方式改造 new Thread( ()-> { System.out.println("多线程启动啦!"); }).start(); } }
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);
}
}
# 所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
# 使用限制不同
如果接口中有且仅有一个抽象方法,可以使用lambda表达式,也可以使用匿名内部类
如果接口有多个抽象方法,只能使用匿名内部类,不能使用lambda表达式
# 实现原理不同
匿名内部类:编译以后产生一个单独的.class字节码文件
Lambda表达式:编译之后没有一个单独的.class字节码文件,对应的字节码会在运行时动态生成
1.8之后可用
aMap.forEach( (k,v)->{System.out.println(k+" "+v);} );
Throwable类是Java语言中所有异常和错误的超类
如果程序出现了问题,我们又没有做任何处理,最终JVM会做默认的处理:
嗐,千篇一律,无需多言
try {
//todo
} catch (Exception e) {
e.printStackTrace();//打印异常信息
} finally {
//todo
}
Message是在构造方法中进行赋值的
方法 | 描述 |
---|---|
getMessage() | 返回此异常的原因 |
toString() | 返回该异常的原因和类名 |
printStackTrace() | 将异常的类名,原因,出现异常代码的位置信息 |
Java中异常分为两大类:编译时异常与运行时异常,也被称为受检异常和非受检异常
所有的RuntimeException类以及子类被称为运行时异常,其他的异常都是编译时异常
并不是所有情况我们都有权限进行异常的处理,有时候可能出现的异常我们是处理不了的
针对这种情况,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:
程序( program):是为完成特定任务、用某种语言编写的一组指令的集合是一段静态的代码
。(程序是静态的)
进程( process):是程序的一次执行过程。正在运行的一个程序,进程
作为资源分配的单位
,在内存中会为每个进程分配不同的内存区域。进程是动态的)是一个动的过程,进程的生命周期:有它自身的产生、存在和消亡的过程
线程( thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程的.
并行:多个CPU同时执行多个任务
并发:一个CPU“同时”执行多个任务(采用时间片切换)
注:异常线程会影响主线程的执行
继承Thread类,重写run方法:
//继承Thread类 public class MyThread extends Thread { //重写run方法 @Override public 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方法 @Override public 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(){ @Override public 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 { @Override public 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方法 @Override public 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
}
synchronized是Java中的关键字,这个关键字的识别是靠JVM来识别完成的呀,是虚拟机级别的。
而Lock锁是API级别的,提供了相应的接口和对应的实现类,这个方式更灵活,表现出来的性能优于之前的方式。
同时也注意是不是同一把锁的问题呀!!!,嫌麻烦就直接使用实现Runnable接口的方式就好了
Lock是接口,不能直接实例化,常采用它的实现类ReentrantLock
来实例化,代码如下:
public class MyRunnable implements Runnable { private int number = 100; private Lock lock = new ReentrantLock(); //重写run方法 @Override public 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个原子性操作组成的:
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);
}
}
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;
}
@Override
public 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;
}
@Override
public 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可以拥有一个同步队列和多个等待队列
class MyThread() { //xxx Lock lock = new ReentrantLock(); Condition produceCondition = lock.newCondition();//生产者等待队列 Condition consumeCondition = lock.newCondition();//消费者等待队列 //xxx public void setProduct() { lock.lock(); try { if (xx) { //生产者进入生产者等待队列 produceCondition.await(); } //唤醒消费者来消费 consumeCondition.signal(); }finally{ lock.unlock(); } } }
Thread提供了currentThread()
静态方法获取当前线程
Thread.currentThread().setName("主线程");
super(name)
:通过构造方法往上传Java实现的是抢占式调度模型,即优先级高的线程可以获得的CPU时间片段多一点。除此之外还有分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片,默认为5,最小为1,最大为10
当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程,即将其他线程进入阻塞状态。
注意:必须先 start,再join才有效
时间以毫秒为单位
Thread.sleep(1000);//线程阻塞一秒钟
将A线程设置为B线程的伴随线程,当B线程停止的时候,A线程也不要存在了,注意,要先将A设置为守护线程再启动A线程
该方法要被废弃了,可以了解一下
Thread.currentThread().stop();
每一个任务都去执行一个新的线程,开销还是很大,因此有了线程池,将任务丢到线程池中去执行即可
目前还用不到,等有需要会补充。
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:输入(读)/输出(写)(Input/Output),从其他位置加载到程序(内存)中称为输入,反之称为输出
流:流是一种抽象概念,是对数据传输的总称,数据在设备间的传输称为流,流的本质是数据传输
按数据的流向分:
按数据类型分:
我们说的IO流的分类一般是按数据类型
分的
InputStream是一个抽象类,是所有输入字节流类的超类
OutputStream也是一个抽象类,是所有字节输出流类的超类,输出流接收输出字符并将其发送到某个接收器
如果文件不存在会自动创建,写数据的时候有三种方式
方法 | 说明 |
---|---|
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的时候是a
fos.write("97".getBytes());
//关闭流并释放系统资源
fos.close();
}
}
换行通过写入换行符即可,不同系统换行符不同:
追加写入的实现,在创建FileOutputStream的时候在后面添加true
,这样就是追加了
FileOutputStream fos = new FileOutputStream("E:\\IdeaProjects\\javatest\\fos.txt",true);
public class Test { public static void main(String[] args) throws IOException { //给定一个路径,调用系统功能创建文件 FileInputStream fis = new FileInputStream("E:\\IdeaProjects\\javatest\\fos.txt"); //读数据,一次读一个字节,如果文件达到末尾则返回-1 int 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:字符输出流的抽象类
方法 | 说明 |
---|---|
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首先会先刷新流,然后再关闭资源
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(); } }
输入简单来说就是读
,是针对程序或者内存来说的,就是从外界读取,可以是从文件中,或者是网络请求中
输出简单来说就是写
,是针对程序或者内存来说的,将已经读取到的或者输入的信息写入外部文件中
字节流:
类名 | 功能 |
---|---|
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");
//使用字节输出流的形式写到文件,是a
ps.write(97);
//使用特有的方法写入文件,看到的就是97
ps.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");
//字符流的形式写到文件,是a
ps.write(97);
ps.write("\r\n");
ps.flush();
//使用特有的方法写入文件,看到的就是97
ps.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); } }
在网络通信协议下,实现网络互联的不同计算机上运行的程序可以进行资源交换,简而言之,用户之间可以发送消息或文件
IP地址是网络中设备的唯一标识,分为IPv4和IPv6两大类,我们使用IPv4进行网络通信,它采用点分十进制表示法,一般长这样192.168.99.1
,127.0.0.1
可以代表本机地址,一般用于测试
命令 | 说明 |
---|---|
ipconfig | 查看本机IP |
ping IP | 检查网络是否连通 |
网络的通信本质上是两个应用程序的通信,每台计算机上有许多程序,每个程序都运行在某一个端口上,端口号可以作为应用程序的唯一标识,普通应用程序要使用1024之后的端口号
在计算机网络中,连接和通信的规则称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换,常见的协议有TCP协议和UDP协议
TCP
协议是面向连接
的通信协议,可以为两台计算机提供可靠无差别
的数据传输,在TCP协议中必须明确客户端与服务器,由客户端向服务器发起连接请求,每次连接要经过三次挥手
UDP
协议是面向无连接
的通信协议,,不管对方有没有收到,反正只负责发出去,由于UDP耗费资源小,通信效率高,通常会用于传输音频、视频、和普通数据以及直播
三次握手以及四次挥手其实就是确保客户端和服务器知道彼此
都具备接收和发送
数据的能力,以及做好了分开的准备,其中,以三次挥手为例。
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);
}
}
Java提供了DatagramSocket类基于UDPP协议的Socket
发送数据的步骤:
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(); } }
接收数据的步骤:
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(); //这里指定长度是消除后面的空字符,因为有时候不够1024 String dataString = new String(data, 0, length); System.out.println("收到:"+dataString); //关闭 socket.close(); } }
客户端和服务端会建立虚拟链路,是通过这个虚拟链路进行通信的。Java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
发送数据的步骤:
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(); } }
接收数据的步骤:
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(); } }
服务器可以通过accept方法获取道连接的socket,这个socket可以获取输入流(客户端传到服务器的信息),然后将输入流写入字符数组。同时这个accept得到的socket对象也可以获取输出流,用同样的套路去给客户端写东西,客户端的socket使用socket.getInputStream()
方法,使用同样的套路去获取服务器的信息
在写的时候可以使用封装了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(); } }
将字节流直接转为字符流
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(); } }
将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服务器内容写入文本文件
是一样的
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方法
泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
它的本质是参数化类型
,也就是说所操作的数据类型被指定为一个参数
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
泛型定义格式:
实参的类型只能是引用数据类型
先看这段代码,这样就报错了!
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.String String 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> {
@Override
public 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>();//报错 } }
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 继承了 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 中每一个元素并执行特定操作 |
链表(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 :是链表结构,增加和删除速度快,就像佛珠
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合
HashSet 允许有 null 值
HashSet 是无序的,即不会记录插入的顺序
HashSet 不是线程安全的
HashSet 实现了 Set 接口
HashSet类常用方法
方法 | 描述 |
---|---|
add() | 添加元素 |
remove(value) | 删除元素 |
contoins(value) | 判断是否存在元素 |
size() | 得到元素个数 |
for-each | 迭代 |
HashSet: 无序
LinkedHashSet: 按照插入顺序
TreeSet: 从小到大排序
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都实现了Map接口,都是键值对保存数据的方式
区别1:
区别2:
Collections是一个类,由静态方法组成,是针对集合框架,容器的工具类,就如同Arrays是数组的工具类
方法 | 功能 |
---|---|
reverse() | 反转 |
shuffle() | 随机打乱 |
sort() | 升序排序 |
swap() | 交换 |
synchronizedList() | 线程安全化 |
retuen的结果会有什么影响:
自定义排序的口诀:
升序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; } @Override public 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接口对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>() { @Override public 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是通过放置的对象去实现Comparable接口的compareTo
方法,升序this在前,降序this在后
Comparator是通过创建的集合去实现匿名内部类,升序前减后,降序后减前
哈希值是JDK根据对象的地址
或者字符串
或者数字
算出来的int类型的数值,可以通过对象的hashCode()
方法获取,默认情况下不同对象的哈希值是不同的,重写hashCode()方法后可以让不同对象拥有相同哈希值
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
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的对象,statement Statement statement = conn.createStatement(); //定义sql,别忘了加分号 String sql = "update account set password=11111 where username=zhangsan;"; //执行sql int count = statement.executeUpdate(sql); //处理结果 System.out.println(count); //释放资源 statement.close(); conn.close(); } }
驱动管理对象
如果是本地的loaclhost还是3306端口号,这个url可以简化为jdbc:mysql:///dbname
数据库连接对象
Statement createStatement()
,执行静态sqlStatement prepareStatement(String sql)
,执行动态sqlsetAutoCommit(boolean autoCommit)
,设置参数为false即开启事务commit()
rollback()
执行sql的对象
boolean execute(String sql)
:可以执行任意sql,但是用的不多
int executeUpdate(String sql)
:执行DML(insert、update、delete)语句和DDL(库、表)语句
ResultSet executeQuery(String sql)
:指定DQL(select)语句
结果集对象,封装查询的结果
ResultSet resultSet = statement.executeQuery(sql);
//和迭代器类似
while (resultSet.next()) {
int anInt = resultSet.getInt(1);
String id = resultSet.getString("id");
}
执行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();
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);
}
}
是apache的开源框架,简化了JDBC的开发步骤,使得我们可以用更少量的代码实现连接数据库的功能,最厉害的是返回值可以直接指定,单个值,列表,Bean,BeanList…,maven页
ResultSetHandler结果集处理类:
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");
}
}
用户每次来不会向系统申请,而是去连接池中使用访问对象,用完再还回来,能节约时间还提高利用率
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/test_go?characterEncoding=UTF-8&useSSL=false username: xxx password: xxxxxx driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 5 min-idle: 5 max-active: 20 test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 30000 filters: stat async-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整合得了
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); // 根据外部窗体实例化JDialog JDialog 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,顺序布局器
容器上的组件水平摆放
加入到容器即可,无需单独指定大小和位置
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); } }
容器上的组件按照上北 下南 左西 右东 中的顺序摆放
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); } }
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,向布局器建议该组件显示的大小.
注 只对部分布局器起作用,比如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需要用到面板,JComboBox这些内容暂时还没学的内容,所以放在后面讲: CardLayout
关键字 | 简介 |
---|---|
JLabel | 标签 |
setIcon | 使用JLabel显示图片 |
JButton | 按钮 |
JCheckBox | 复选框 |
JRadioButton | 单选框 |
ButtonGroup | 按钮组 |
JComboBox | 下拉框 |
JOptionPane | 对话框 |
JTextField | 文本框 |
JPasswordField | 密码框 |
JTextArea | 文本域 |
JProgressBar | 进度条 |
JFileChooser | 文件选择器 |
与python类似,可以绑定事件,代码用到了再去查
JPanel即为基本面板
面板和JFrame一样都是容器,不过面板一般用来充当中间容器,把组件放在面板上,然后再把面板放在窗体上。
一旦移动一个面板,其上面的组件,就会全部统一跟着移动,采用这种方式,便于进行整体界面的设计
JFrame上有一层面板,叫做ContentPane
平时通过f.add()向JFrame增加组件,其实是向JFrame上的 ContentPane加东西
ContentPane这个毛毯其实又放在了其他的毛毯上面
创建一个水平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,右边是p2 JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, pLeft, pRight); // 设置分割条的位置 sp.setDividerLocation(80); // 把sp当作ContentPane f.setContentPane(sp); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
使用带滚动条的面板有两种方式
在创建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); } }
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 布局器 很像TabbedPanel ,在本例里面上面是一个下拉框,下面是一个CardLayerout 的JPanel
这个JPanel里有两个面板,可以通过CardLayerout方便的切换
先建立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); } }
swing没有自带的日期控件,需要第三方的类
有三种线程
1、初始化线程
初始化线程用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。
2、事件调度线程
通过事件监听的学习,我们了解到Swing是一个事件驱动的模型,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。比如点击一个按钮,对应的ActionListener.actionPerformed 方法中的代码,就是在事件调度线程 Event Dispatch Thread中执行的。
3、长耗时任务线程
有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的 长耗时任务线程中进行
风格和皮肤可以自由切换的,还可以设置成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();
}
注解与反射是所有框架的底层,例如Mybatis、Spring等等,注解(Annotation)是给程序看的,注释(Comment)是给人看的,注解是Java基础中最简单的一章,不用感觉很难。
注解的作用
注解由JDK5.0引入,它不是程序本身,但是可以对程序做出解释,可以被其他程序(例如:编译器)读取,注解不是必须的,但有时候因为程序需要所以要写
注解的格式
以@注解名
的形式存在,也可以添加一些参数值,例如:@SuppressWarnings(value="unchecked")
注解在哪里使用
@Override重写
@Deprecated方法废弃
@SuppressWarnings(value=“all”)镇压警告信息
import java.lang.annotation.*; //测试元注解 public class Test { @MyAnnotation public 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
接口
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; }
在Object类中定义了以下方法:
public final Class getClass()
以上的方法返回值的类型是一个C|ass类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
常用的方法:
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 = "学生"; } }
主动引用
java类的初始化阶段,虚拟机规范严格规定了5种情况必须立即对类进行初始化。
被动引用
除了上述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(); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。