javase 学习笔记
老杜,永远的神!!!
java 语言的特性
1、简单性。java中支持单继承,c++中支持多继承,Java中没有指针。
2、健壮性。java中有自动垃圾回收机制(GC),jvm会将堆区没有变量指向的对象回收。
3、可移植。一次编译,到处运行。我们只需要编译成字节码文件,然后在windows或linux系统安装jre运行环境即可。
4、多线程。处理程序更加快速。
5、安全性。开源的代码对程序员来说更加安全。
JDK 安装
下载好JDK(java开发工具箱)后,如果需要在DOS命令行下编译运行java文件,则需要提前为系统配置环境变量,将JDK下的bin目录添加到环境变量中的PATH路径下。
JDK中包含jre和jvm,jre中包含jvm。如果需要开发的话,则需要安装JDK,如果只需要运行字节码文件,则只需要安装对应系统的jre即可。此处体现了java文件的可移植性。
java 的编译运行
在DOS窗口下,javac+源文件路径 进行编译
java +类名 进行运行
java 程序的运行过程
使用javac进行编译,生成字节码文件,然后使用java命令调用JVM虚拟机,此时JVM会启动类加载器(classloader),在当前目录下对字节码文件进行查找,找到后类加载器会将此文件交给JVM,JVM将其翻译为操作系统能读懂的二进制文件。(从JDK11之后包括JDK11,可以直接使用 java + 源文件路径进行编译运行)
java 中的标识符
在java中,标识符是我们可以修改的东西,并非系统定义。
是标识符的有:
类名、方法名、变量名、常量名、接口名。
标识符可以有:
数字、字母、下划线、美元符号组成,但是数字不能当作开头。
严格区分大小写,长度不做限制。
标识符的命名规范:
类名、接口名:遵循驼峰命名法(每个单词的首字母大写,其余小写)。
变量名、方法名:第一个单词全部小写,后面每个单词遵循驼峰命名法。
常量名:每个单词全部大写,中间用下划线分割。
变量
内存中最基本的存储单元,是用来盛放数据的盒子。
变量三要素:
数据类型、变量名、字面量(数据)。
int i = 1;
变量的位置不同,会导致变量的作用域不同。大作用域包纳小作用域,比如成员变量作用域包括局部变量作用域。
数据类型
java中数据类型整体分为两种。基本数据类型和引用数据类型。基本数据类型是系统默认的,引用数据类型除了String,其他的基本都是用户自己定义。
基本数据类型:
byte(1) 、short(2)、int(4)、long(8) 、float(4)、double(8)、boolean(1)、char(2)
引用数据类型:
String(String是由多个char字符拼接而成)。。。。
其中:
byte 表示范围:-128~127
short 表示范围:-32768~32767
int 表示范围:-2147483648~2147483647
char 表示范围:0~65535
在取值范围内可以直接赋值。除了boolean类型,其他基本类型数据可以相互转换。
小容量直接可以转换成大容量,被称为:自动类型转换。
大容量可以转换成小容量,被称为:强制类型转换。(会损失精度)
int i =(int)2147483648; //超出Int范围强转成Int
输入整型数据会被默认当作int类型处理,输入浮点数据会被当做double类型处理。
float i = 5.0F;
long j = 2147483648L; //不加L会被当做int类型数据,int赋值给long会报错
当byte、short、int、char混合运算时,jvm会将其先转换成int类型然后进行转换
字符编码
最初的计算机编码是ASCII码,虽然采用一个字节来存储ASCII码,但是第一位被舍弃掉了,因此ASCII码最多只有128个。
后来出现了ISO-8859-1编码,也就是我们常用的latin-1,但是此种编码并不支持中文,再后来出现了GB2312、GBK、GB18030(三种字符编码容量大小从左到右依次增大)这些编码,中文才被支持。
java中使用Unicode编码(为了支持全球文字),这种编码支持utf8、utf16、utf32······
繁体中文的字符编码是big5。
逻辑运算符
算术运算符: 关系运算符: 逻辑运算符: 三目运算符:
±*/%+±- > >= < <= == != &|! && || 布尔表达式?表达式1:表达式2
接收键盘输入数据
java.util.Scanner s = new java.util.Scanner(System.in);
int i = s.nextInt();//接收整数
String j = s.next();//接收字符串
循环、转向、分支语句
循环包括 for、while、do while 三种,转向包括break、continue、return,选择包括if和switch
for(i=0;i<10;i++){if(i==5){continue;//当i等于5时跳出本次循环,进行下一次循环system.out.println("这是第"+i+"次打印");}if(i==9){system.out.println("这是第"+i+"次打印");break;//当i等于9时终止本层循环}
}
switch(值){//String、int类型都可以case 值1:java语句;break;//如果不写break会出现case击穿,一直击穿到找到break或者找到分支最后才能结束case 值2:java语句;break;············defaultjava语句;
}
do…while循环必定会被执行一次,break终止本层循环,continue跳出本次循环,进行下一次循环,return 用来返回结果和终止当前方法。
方法
可以重复使用,并具有特定功能的代码片段。
定义:
[修饰符列表] 返回值类型 方法名(形式参数列表){方法体;}
方法体中的语句自上至下顺序执行。
当方法和入口在同一个类中,可以直接调用方法。如果不在同一个类,则需要输入‘’类名.方法‘’调用。
当我们使用递归方法时务必保证最后要有终止条件,否则递归一直调用,会产生栈内存溢出错误。
jvm内存空间
JVM内存空间主要有三部分,堆区、栈、方法区。
方法区:
JVM启动后,会调用类加载器将字节码文件加载到方法区,形成代码片段,此时的方法区存放的都是未运行字节码文件。此外,在类加载时也会将我们的静态变量赋值,并且也会执行我们的静态代码块。
栈区:
当我们调用方法时,JVM会将对应的方法加载到栈区,这个过程叫做压栈。如果正在执行的代码片段(方法)运行中又调用了方法, JVM则会继续压栈,直到调用最后一个方法后,并且执行完成后,开始从最后一个方法,依次反向执行该方法前一个方法代码,直到结束,这个过程叫做弹栈。因此,栈区存放了正在运行的方法和局部变量。(栈区的栈帧永远指向栈顶,因此处于栈顶的方法为活跃状态)
堆区:
当我们堆区的方法正在执行的过程中,可能会创建对象,我们创建的这个对象,就会被JVM放到堆区,创建好对象后,JVM会为我们返回一个内存地址,该内存地址即为创建对象的地址,这个地址会返回给我们的引用。如果我们的引用没有被释放则我们对应在堆区的对象也不会被释放。因此,堆区存放了我们创建的对象,和对象中的实例变量。
User u = new User();//其中new User();是对象,u是引用,u前面的User是该变量的数据类型。
面向对象
对于C语言来说,是完全面向对象的。面向对象虽然在编写代码时舒服,但是代码与代码之间耦合度高,一环套一环,后期拓展起来非常麻烦。C++是一种半面对对象半面对过程的代码,我们的JVM底层代码就是使用C++实现的。
对于java语言,是完全面向对象的。在我们现实生活中,所接触的都是面对对象,比如:小明打篮球。小明是一个对象,篮球也是一个对象,而他们之间有一个打的行为。采用面向对象编程开发,可以更加方便我们编程。
面向对象术语:
OOA 面向对象分析
OOD 面向对象设计
OOP 面向对象编程
面向对象三大特征:
封装、继承和多态。
封装——>继承——>多态(层层相扣)
类和对象
类:现实世界中的某些事物具有相同特征,我们将这些特征抽象成一个概念,这个概念就叫做类。
对象:现实中存在的个体。(实例是对象的另一种称谓)
将对象的特征抽取成类的过程称为抽象。用类创建对象的过程称为实例化。
类里面的变量被称为实例变量,方法被称为实例方法(也可以有其他类型的方法),实例方法不能被static修饰,实例方法的调用需要‘’引用.方法名‘’调用。类中的变量实际就是属性,方法就是动作。 A has a B 说明A有一个属性是B。
对象是我们new出来的,在上面已经描述过对象的创建方法,在此不再赘述。
构造方法
当我们创建好一个类后,如果我们不为其创建构造方法,则系统会默认生成一个无参构造方法(也被称为缺省构造器),但是如果我们创建了一个有参构造方法,则系统不会再为我们创建无参构造方法,因此我们需要将有参和无参构造方法都创建好。
class User(){int id;String name;public User(){}//无参构造public User(int id,String name){this.id = id;//this指代的是当前对象的地址this.name = name;}
}
封装
我们类中属性一般都推荐使用private修饰符进行修饰,被private修饰的变量只能在本类中进行使用,在别的类则需要使用get()和set()方法进行调用,我们可以在get()和set()方法中对程序调用变量和赋值进行限制。使用封装可以让我们更好的保护类的内部。我们只需要调用入口就可以访问了。
static关键字修饰的变量我们可以通过‘’类名.变量名‘’或是‘’引用.变量名进行访问‘’,但是静态变量我们推荐前者的调用形式,静态变量一般赋初值后就不会再改变,因此我们不需要每次new对象时都进行赋值(在类加载时就已经被赋初值,存放在方法区)。
class User{private int id;public User(){}//无参构造public User(int id){this.id = id;//this指代的是当前对象的地址}public int getId(){return this.id;}public void setId(int id){if(id==123)return ;this.id=id;//说明只有输入的id不是123时才能成功更改id值}
}class Test{public static void main(String[] args){User u = new User();//创建User对象,调用无参构造u.setId(456);System.out.println(u.getId());//打印id}
}
this关键字只在本类中使用,大部分情况下可以省略,但是如果传进来的形参和要赋值的形参名字相同则不能省略,比如this.name=name就不可以省略,否则就是自己给自己赋值。this()放在构造方法的第一行时,代表可以调用有参构造进行传值。
class User{int id;public User(){this(678);//在无参构造方法中调用有参构造,进行赋值}//无参构造public User(int id){this.id = id;//this指代的是当前对象的地址}}
继承
子类继承父类的属性和方法(构造方法不能被继承,由父类继承下来的私有属性不能直接访问,但是可以使用get和set方法),实现代码复用。继承也为后来的多态打下了基础,有了继承才会有多态。在java语言中,类只支持单继承,所有的类最终都会继承Object类,这个类是所有类的祖先。凡是能被A is a B 描述的,则A是子类,B是父类。
class Anmial{private String name;public void move(){//move方法System.out.println("动物在行走");}public Anmial(){}public Anmial(String name){this.name=name;}public String getName(){return this.name;}public void setName(String name){this.name=name;}
}class Cat extends Anmial{public void move(){//重写父类的move()方法System.out.println(this.getName()+"在行走");}
}class Test{public static void main(String[] args){Cat cat = new Cat();cat.setName("猫");//使用setName方法为继承父类的私有属性赋值cat.move();}
}
重写(覆盖)
当我们继承父类方法后如果父类方法不能满足我们的需求,则需要我们对父类方法进行重写。重写是为了拓展需求。
注意:
由于构造方法不能被继承,因此不能被重写。
重写后的方法权限要比父类的高,抛出的异常要比父类的少。
重写的是方法,不是属性,私有方法不能被重写,静态方法重写没有意义。
重写时要求返回值类型,方法名,参数列表都相同,方法体不同。(方法重载要求变量值相同,形式参数不同(形参个数、顺序、类型),方法体类似,方法重载是为了解决多个方法拥有相似的功能。)
多态
编译和运行时有两种形态:编译时静态绑定,运行时动态绑定。
多态的使用
class Anmial{private String name;public void move(){//move方法System.out.println("动物在行走");}public Anmial(){}public Anmial(String name){this.name=name;}public String getName(){return this.name;}public void setName(String name){this.name=name;}
}class Cat extends Anmial{public void move(){//重写父类的move()方法System.out.println(this.getName()+"在行走");}
}class Test{public static void main(String[] args){Anmial cat = new Cat();if(cat instanceof Cat){Cat x = (Cat)cat;x.setName("猫");//使用setName方法为继承父类的私有属性赋值x.move();}}
}
在Test类中, 26行Anmial cat = new Cat(),Anmial 是父类型,引用指向了子类型Cat。在编译时从左边编译,则认为变量cat为Anmial类型,则28行在编译时检测到的是父类的move方法;但是在运行时从右往左运行,则会调用子类重写后的move方法,实现多态的应用。
Anmial cat = new Cat(),这种new对象方法被称为向上转型,Cat是子类,被转成了父类Anmial类型。
Cat x = (Cat)cat,这种被称作向下转型,由于向下转型时可能会产生错误(如果cat引用指向的对象地址并不是Cat类型),因此需要在向下转型时用 “A instanceof 下转的类型“进行判断,类型一致才可以强转。
注意:
使用多态的前提:必须有继承关系。
super
super()方法可以主动调用父类有参或是无参构造方法(如果不传参数,默认调用无参构造方法),如果没有父类,默认继承Oject类(因此,如果有多层继承的话,调用super()方法时,也会一层一层往上寻找父类构造方法,并按栈的定义顺序执行)。
只要有无参构造方法就会有super()方法,当我们创建无参构造方法时,如果内部什么也不填写,则默认为其添加super()方法。
super()方法只能放在构造方法的第一行,this()方法也只能放在构造方法的第一行,因此两种方法在一个构造方法只能存在一种。
super也代表父类的地址,因此可以用”super.“来调用父类方法或者属性(只能用于实例方法)。
抽象类
抽象类:是由两个或多个已经经过抽象的类,再次进行抽象形成的类。比如:储蓄卡类、信用卡类可以进一步抽象成银行卡类。
抽象(abstract)类中可以有抽象方法(抽象方法没有方法体),也可以有非抽象方法,但是如果一个类有抽象方法,那么他一定是抽象类。
抽象类虽然不能被实例化(类抽象出来的类不可以实例化),但是可以被继承,但是如果子类继承了父类的抽象方法,则一定要重写父类中的抽象方法。
abstract class Card{private int id;abstract void printId();//抽象方法没有方法体public Card(int id) {this.id = id;}public Card() {}public int getId() {return id;}public void setId(int id) {this.id = id;}
}class Creditcard extends Card{public void printId(){System.out.println("信用卡ID是"+this.getId());}
}
class Depositcard extends Card{public void printId() {System.out.println("储蓄卡ID是" + this.getId());}
}class Test{public static void main(String[] args) {Card c1 = new Creditcard();c1.setId(100001);Card c2 = new Depositcard();c2.setId(200001);c1.printId();c2.printId();}
}
final
被final修饰的类不能被继承,修饰的方法不能被重写,修饰的变量只能赋值一次。
final和static合用创建的被称为常量,如果我们不为其赋初值,则在new对象的时候,系统调用无参构造方法,会自动为其赋初值,但是一旦赋值后,则无法修改,这个常量也就失去了意义,因此需要我们在程序为它赋初值之前就为其赋值。我们可以在有参构造方法中为其传值,也可以直接在常量名后直接赋值。(常量命名规范:所有字母全部大写,每个单词之间用下划线连接)
由于final修饰的类不能被继承,而抽象类一般都是用来继承的,因此,final和抽象类(abstract)不能连用。
接口
接口中只能存放常量和抽象方法,因此我们可以将常量的public static final 和抽象方法的 public abstract省略,程序在调用时会自动补上。
接口与类不同,类只支持单继承,而接口支持多继承,当我们创建好类及其内部的方法后,一定要为其常量赋值,为接口的抽象方法进行实现(implements),若未实现抽象方法,程序则无法正常运行。(一个类可以实现多个接口)
接口的应用是为了减轻调用者和实现者的耦合度,接口就像一个中间人,调用者只需要关心接口有什么功能可用,不需要关心该功能如何实现。把他们两个分开了。
类所拥有的属性可以被接口实现。
--------------------------------------------------------------------------创建接口
interface CardNature{//用接口来设置卡片有什么内容void printColour();void printSize();
}
---------------------------------------------------------------------------实现接口方法
class Card implements CardNature{//用来实现卡片接口的方法private String cardColour;private String cardSize;public Card(String cardColour, String cardSize) {this.cardColour = cardColour;this.cardSize = cardSize;}public Card() {}public String getCardColour() {return cardColour;}public void setCardColour(String cardColour) {this.cardColour = cardColour;}public String getCardSize() {return cardSize;}public void setCardSize(String cardSize) {this.cardSize = cardSize;}public void printColour(){System.out.println("卡片的颜色是"+this.getCardColour());}public void printSize(){System.out.println("卡片尺寸是"+this.getCardSize());}
}
-------------------------------------------------------------------------创建调用者
class User{//用户只看到接口的东西,不关心如何实现CardNature cardNature;public User(CardNature cardNature) {this.cardNature = cardNature;}public CardNature getCardNature() {return cardNature;}public void setCardNature(CardNature cardNature) {this.cardNature = cardNature;}public void seeColour(){cardNature.printColour();}public void seeSize(){cardNature.printSize();}
}
---------------------------------------------------------------------------测试
class Test{public static void main(String[] args) {CardNature c = new Card();//向上转型if(c instanceof Card){//判断c是否为Card类型,是的话就向下转型Card cc = (Card)c;//向下转型cc.setCardColour("白色");//设置属性cc.setCardSize("10cm");User u =new User(cc);u.seeColour();//用户看卡片颜色u.seeSize();//用户看卡片尺寸}}
}
数组
数组是在一段连续的空间内存放多个相同数据类型数据的存储方式,存放的数据类型可以是基本数据类型,也可以是引用数据类型,如果存储的是基本数据类型,则数组空间里存放的都是数据,如果是引用数据类型,则存放的都是对象的地址,我们可以通过地址来找到这个对象。
由于数组的存储空间是连续的,因此我们创建的数组时,程序返回给数组的引用是这个数组的初始地址。由于我们已经知道该地址存放何种数据类型,因此我们可以通过首地址和数组中的值的下标来确定这个值的地址(初始地址+(下标+1)*数据类型长度)。
数组的初始化有两种形式,一种是静态初始化,一种是动态初始化。数组的遍历只需要使用for循环即可。
public class Test{public static void main(String[] args) {int[] arr1 = new int[]{1,4,6,4,3};//静态初始化int[] arr2 = new int[5];//动态初始化for (int i=0;i<5;i++){arr2[i]=(int)(Math.random()*10);}String[] arr3 = new String[5];//引用数据类型数组arr3[0]="小明";arr3[1]="小花";arr3[2]="小黄";for (int i=0;i<5;i++)System.out.println(arr1[i]);System.out.println("---------------------------------");for (int i=0;i<5;i++)System.out.println(arr2[i]);System.out.println("---------------------------------");for (int i=0;i<5;i++)System.out.println(arr3[i]);}
}
数组的拷贝,可以调用库中的System.arraycopy()方法。(System.arraycopy(拷贝源,开始位置,拷贝目的地,开始位置,长度))。数组有一个缺点,一旦确定长度,则不能改变,因此,当我们的数组长度不能满足我们的需求时,我们需要new一个更大容量的数组,使用System.arraycopy()方法,将原数组中的值拷贝到新数组,原数组可以释放。
public class Test{public static void main(String[] args) {int[] arr1 = new int[]{1, 4, 6, 4, 3};int[] arr2 = new int[10];System.arraycopy(arr1, 0, arr2, 0, arr1.length);for (int i=0;i<5;i++)System.out.println(arr1[i]);System.out.println("--------------------------------------");for (int i=0;i<10;i++)System.out.println(arr2[i]);arr1=null;}
}
数组中的System.arraysort
访问控制权限
访问控制权限 | 本类 | 同包 | 子类 | 跨包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
默认(什么也不加) | √ | √ | ||
private | √ |
访问控制权限修饰符可以修饰什么?
属性(4个都能使用)
方法(4个都能使用)
类(public和默认可以用)
接口(public和默认可以用)
String、StringBuffer、StringBuilder
String数据类型属于引用数据类型,但是在使用过程中我们不需要new对象,可以直接为其赋值。
被双引号引起来的字符内容是不可变的。(底层存放字符的byte数组被final修饰,一旦放入字符串常量池,则无法修改值)
字符串常量池:
位于方法区,里面存放了我们创建的字符串,每创建一个字符串就会生成一个字符串对象,并且无法修改,如果是两个不同的字符串相加,则会生成三个字符串。这些字符串都有自己独特的存储地址。
String str1 = "123";//创建第一个字符串对象
String str2 = "456";//创建第二个字符串对象
String str = str1+str2//创建第三个字符串对象String str3 = "123";//不会创建新的字符串,因为"123"字符串已经在第1行被创建过了
String str4 = "123";
String strr = str3+str4//创建第四个字符串对象
整数型常量池:
由于-128~127这些数据,经常会被系统调用,因此JVM在方法区开辟了一块空间专门存放这些常用整数,叫做整数型常量区。方便我们 调用。这些数据都有自己独特的地址。
如果我们频繁的对字符串进行拼接,则程序会不停的在静态常量池生成字符串对象,降低效率,而StringBuffer则可解决这种问题,StringBuffer底层调用了Byte数组,这个数组默认长度为16,我们可以使用System.append()方法往数组中添加字符,这些字符并不会放入字符串常量池,当我们存放满Byte数组后,底层会自动调用Arrayscopy()方法对数组进行扩容。
StringBuffer和Stringbuilder区别:
StringBuffer在多线程运行环境下是安全的。
StringBuilder在多线程运行环境下是不安全的。但是在单线程中它的效率是比StringBuffer高的。
String方法
import java.util.Locale;
public class StringTest {public static void main(String[] args) {String str = new String(" QWEdw#21#23 ");System.out.println(str);//打印字符串到控制台System.out.println("字符串第二位:"+str.charAt(2));System.out.println("2第一次出现的位置:"+str.indexOf("2"));System.out.println("2最后一次出现的位置"+str.lastIndexOf("2"));System.out.println("字母全部转换成小写:"+str.toLowerCase());System.out.println("字母全部转换成大写:"+str.toUpperCase());System.out.println("截取第二位之后的字符串:"+str.substring(2));System.out.println("截取2到6的字符串:"+str.substring(2,6));System.out.println("判断字符串是否为空:"+str.isEmpty());System.out.println("除去前后空白:"+str.trim());System.out.println("将“2”替换成“1”:"+str.replace("2","1"));System.out.println("判断字符串中是否有“dw”字符串:"+str.contains("dw"));System.out.println("获取字符串长度:"+str.length());System.out.println("判断字符串是否以“QW”开头:"+str.startsWith(" QW"));System.out.println("判断字符串是否以“23”结束:"+str.endsWith("23 "));System.out.println("判断字符串是否包含“dw”字符串:"+str.contains("dw"));System.out.println("以”#“分割字符串");String[] s = str.split("#");for(int i=0;i<s.length;i++)System.out.println(s[i]);System.out.println("将字符串转换成char数组");char[] c = str.toCharArray();for(int i=0;i<c.length;i++)System.out.print(c[i]+" ");System.out.println();System.out.println("-----------------------------------------------------");String str1 = new String("aSdAB123");String str2 = new String("asDAB123");System.out.println(str1);System.out.println(str2);System.out.println("比较两个字符串是否相等:"+str1.equals(str2));System.out.println("比较两个字符串是否相等,不区分大小写:"+str1.equalsIgnoreCase(str2));}
}
异常处理
Throwable是一个类,它的子类是Error和Exception(Exception类下有一个RunTimeException(运行时异常)类和编译时异常类),其中Error类一旦发生,JVM会直接退出,无法挽回。而Exception类是我们可以处理的。
Exception异常分为:编译时异常、运行时异常(RunTimeException)。其中编译时异常是我们必须要处理的,如果我们不处理,则代码无法被编译器通过,而运行时异常,我们可以处理,也可以不处理。无论是编译时异常或是运行时异常都发生在运行期。
我们处理编译时异常时一般有两种方式,一种是“向上抛”(throws),另外一种是直接在当前处理(try…catch)。
我们在方法入口出不能向上抛,必须处理。我们不能把异常交给JVM处理。
如果使用throws抛异常的话,则发生异常后的代码片段都不会执行,而如果使用try…catch处理异常,则try…catch之后的代码片段依然会执行。
自定义异常类
public class IllegalInputException extends Exception{//手动创建异常类public IllegalInputException(){}public IllegalInputException(String s){super(s);}
}
测试异常
import java.util.Scanner;
//用户输入name和密码,判断name是否合法(6-14位),如果不合法,报出我们定义的异常
public class RegisterTest {public static void main(String[] args) {Scanner s = new Scanner(System.in);System.out.println("请输入用户名:");String s1 = s.next();System.out.println("请输入密码:");String s2 = s.next();UserService u =new UserService();try {u.register(s1,s2);} catch (IllegalInputException e) {e.getMessage();System.out.println(e);//打印异常信息}}
}class UserService { //定义类private String username;private String password;public void register(String username,String password) throws IllegalInputException {if(username.length()>14||username.length()<6)throw new IllegalInputException("输入数值不合法");//手动抛出异常,上面已经定义过这个异常,并输入异常信息this.username=username;this.password=password;}
}
在try不仅可以和catch搭配,还可以和finally搭配(try…catch、try…finally、try…catch…finally)。放在finally里的代码片段一定会执行。
try中返回了结果,finally中的语句块仍会正常执行,JVM会将i中的值找一个变量先存起来,此时然后继续执行finally中的语句快。最后返回第一个执行的return语句,此时返回的不是i而是存值的那个变量。(java语言,自上至下执行)
public class TryTest { //结果是1public static void main(String[] args) {int i=1;System.out.println(question(i));}public static int question(int i){try{return i;}finally {return ++i;}}
}
集合
集合主要有三类:list、map、set。
集合只能放对象的地址。
Map可以转Set(使用entrySet或keySet,keySet需要在遍历时获取value值),Set可以转List(new集合时把Set集合放进去),但是反过来转会有问题。
集合的遍历一般使用迭代器、增强for循环。
Iterable(接口)的子类是Collection(接口),Collection的子类Set(接口)和List(接口)
List(有序,可重复)下有:ArrayList、LinkedList、Vector等常用类。
Set(无序,不可重复)下有:HashSet、TreeSet等常用类。(TreeSet的父类是SortedSet)。
Map(无序,不可重复)下有:HashMap、Hashtable、properties(是Hashtable的子类)、TreeMap(父类是SortedMap)等常用类。
ArrayList:
ArrayList集合底层是一个Object[]数组,并且这个Object[]数组长度初始化为10,并且这个容量是在赋第一个值时,JVM赋给Object[]数组的。当Object数组容量满后,此数组会以1.5倍的关系扩容。因为ArrayList底层是由数组实现,因此,查询和修改效率较高。但是ArrayList是非线程安全的。
mport java.util.*;public class CollectionTest {public static void main(String[] args) {Collection c = new ArrayList();c.add("123");c.add(100);String s = new String("SAD");c.add(s);System.out.println(c.size());c.remove(100);System.out.println(c.size());Iterator it =c.iterator();while (it.hasNext())//使用迭代器遍历集合System.out.println(it.next());
LinkedList:
LinkedList集合底层是一个双向链表,链表没有初始长度,可以一直扩容。并且链表的增删效率较高。
List<String> l =new LinkedList<>();l.add("asd");l.add("dsa");l.add("12233");l.set(2,"qwe");//改值System.out.println(l.lastIndexOf("dsa"));//判断最后出现"dsa"的位置Iterator it2=l.iterator();while (it2.hasNext())//使用迭代器遍历System.out.println(it2.next());for (String ss:l//使用foreach遍历) {System.out.println(ss);}
Vector:
Vector集合底层是一个Object[]数组,初始化容量为10,当数组容量满后,此数组会以2倍的关系扩容。因为Vector底层是由数组实现,因此,查询和修改效率较高。但是Vector是线程安全的。由于Vector线程安全,因此效率没有ArrayList高。
HashSet:
HashSet集合底层是一个HashMap,并且只使用了HashMap的key部分。这个HashMap集合的初始长度是16,当这个集合容量充满0.75时,HashMap会自动扩容会原集合的2倍。并且HashSet是非线程安全的。(HashTable是线程安全的)
TreeSet:
TreeSet(可排序)底层用的是TreeMap,并且只使用了HashMap的key部分。这个HashMap集合的初始长度是11,当这个集合容量充满0.75时,HashMap会自动扩容会原集合的2倍加1。如果使用TreeSet集合,并且存放的数据类型是自定义的话,则必须实现comparable接口的 compareTo方法。(这个方法负责排序)
import java.util.Iterator;
import java.util.TreeSet;public class TreeSetTest {public static void main(String[] args) {TreeSet treeSet = new TreeSet();treeSet.add("123");treeSet.add("456");treeSet.add("100");System.out.println(treeSet.size());treeSet.add("456");System.out.println(treeSet.size());treeSet.remove("456");System.out.println(treeSet.size());Iterator iterator = treeSet.iterator();while (iterator.hasNext())System.out.println(iterator.next());}
}
Properties:
Properties的Key和value都必须使用字符串类型。
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;public class PropertiesTest {public static void main(String[] args) {Properties properties = new Properties();properties.setProperty("123","张三");properties.setProperty("124","李四");properties.setProperty("93","王五");properties.setProperty("25","赵六");System.out.println(properties.getProperty("123"));Set<Map.Entry<Object, Object>> entries = properties.entrySet();//将propertoes集合转换成set集合方标遍历Iterator<Map.Entry<Object, Object>> iterator = entries.iterator();while (iterator.hasNext()){Map.Entry<Object, Object> next = iterator.next();Object key = next.getKey();Object value = next.getValue();System.out.println(key+"--->"+value);}}
}
HashMap:
HashMap底层是哈希表。哈希表底层由一个数组和若干个单链表组成。因此HashMap集合了链表和数组的优点和缺点。哈希表的初始长度为16,当这个哈希表容量充满0.75时,HashMap会自动扩容会原表的2倍。如果使用HashMap集合,并且存放的数据类型是自定义的话,必须要重写eauals和hashcode方法。在存数据时哈希表会先使用hashcode方法,将key转换成哈希值,并经过哈希算法转换成数组下标,并和该下标下所有的节点进行比较(equals),如果未重复,则可以插入。非线程安全。
import java.util.*;public class MapTest {public static void main(String[] args) {HashMap<Integer,String> m =new HashMap<>();m.put(1,"zhangsan");m.put(2,"liwang");m.put(3,"wwangwu");System.out.println(m.size());System.out.println(m.get(2));Set<Integer> keys = m.keySet();//先得到key,然后在遍历的过程中用get方法获取valueIterator<Integer> iterator = keys.iterator();while(iterator.hasNext()) {Integer integer = iterator.next();String s = m.get(integer);System.out.println(integer+"="+s);}for (Integer i:keys) {String s = m.get(i);System.out.println(i+"="+s);}System.out.println("-----------------");Set<Map.Entry<Integer, String>> entries = m.entrySet();Iterator<Map.Entry<Integer, String>> iterator1 = entries.iterator();while (iterator1.hasNext()){System.out.println(iterator1.next());}for (Map.Entry<Integer, String> i:entries) {System.out.println(i);}}
}
使用自定义类型的key,必须重写hashcode和equals和toString方法。key都相同值不同会覆盖。
import java.util.*;public class HashMapTest {public static void main(String[] args) {HashMap<Student, Integer> hashMap = new HashMap<>();hashMap.put(new Student("zhangsan"),5);hashMap.put(new Student("lisi"),3);hashMap.put(new Student("wangwu"),2);hashMap.put(new Student("zhaoliu"),1);hashMap.put(new Student("wangwu"),2);Set<Map.Entry<Student, Integer>> entries = hashMap.entrySet();Iterator<Map.Entry<Student, Integer>> iterator = entries.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}
class Student{String name;public Student(String name) {this.name = name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}
Hashtable:
Hashtable初始话容量为11,这个集合容量充满0.75时,Hashtable会自动扩容会原集合的2倍+1。线程安全。
TreeMap:
TreeMap底层是一个二叉树,并且这个二叉树是有序的,如果使用TreeMap集合,并且存放的数据类型是自定义的话,则必须实现comparable接口的 compareTo方法。
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;public class TreeMapTest {public static void main(String[] args) {TreeMap<Integer, String> treeMap = new TreeMap<>();treeMap.put(1,"zhangsan");treeMap.put(5,"lisi");treeMap.put(4,"zhaoliu");treeMap.put(4,"maqi");Set<Integer> objects = treeMap.keySet();Iterator<Integer> iterator = objects.iterator();while (iterator.hasNext()) {Integer next = iterator.next();System.out.println(next+"="+treeMap.get(next));}System.out.println("---------------------");Set<Map.Entry<Integer, String>> entries = treeMap.entrySet();Iterator<Map.Entry<Integer, String>> entryIterator = entries.iterator();for (Map.Entry<Integer, String> s:entries) {System.out.println(s.getKey()+"="+s.getValue());}}
}
HashMap中自定义类型(key)必须实现comparable接口(依次比key,第一个相同值找第二个比)和toString方法。key都相同值不同会覆盖。
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;public class TreeMapTest1 {public static void main(String[] args) {TreeMap<User, Integer> treeMap = new TreeMap<>();treeMap.put(new User(5,"zhangsan"),3);treeMap.put(new User(4,"lisi"),2);treeMap.put(new User(4,"wangwu"),1);treeMap.put(new User(3,"zhaoliu"),5);treeMap.put(new User(3,"zhaoliu"),6);Set<Map.Entry<User, Integer>> entries = treeMap.entrySet();Iterator<Map.Entry<User, Integer>> iterator = entries.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}
class User implements Comparable<User>{//实现Comparable接口int id;String name;public User(int id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}@Overridepublic int compareTo(User o) {//重写compareTO方法if(this.id==o.id)return this.name.compareTo(o.name);return this.id-o.id;}
}
IO流
IO流是用来进行硬盘和内存交互的,IO流就像一个管道,将其联通。
IO流有4大类:
InputStream、OutputStream、Reader、Writer。其中前两类是字节流,后两类是字符流。并且1和3是输入流,2和4是输出流。
当我们用完流后,一定要记得关闭流(close()),而输出流在结束时还要使用flash()方法刷新管道,将管道内残留的数据清空,保证我们取得的数据完整。
常用流:
FileInputStream、FileOutputStream ————>文件字节流
FileReader、FileWriter————>文件字符流
BufferedInputStream、BufferedOutputStream————>缓冲字节流
BufferedReader、BufferedWirter————>缓冲字符流
InputStreamReader、OutputStreamWirter————>字节转换流
PrintStream、PrintWirter————>输出流
DataInputStream、DataOutputStream————>数据流
ObjectInputStream、ObjectOutputStream————>对象流
其中常用的有:
FileInputStream、FileOutputStream、ObjectInputStream、ObjectOutputStream、PrintStream
FileInputStream:
由于是字节流,我们使用一个字节数组将读取到的数据进行存放,字节数组长度为4,则表明读一次最多4个字节,在循环外部定义一个标志变量,保存读到数据的个数(也就是长度),使用一个while循环来判断文件中是否还有数据,没有,则循环结束。打印我们每次读到的数据。(如果我们不设置初始的数组,则我们标志变量每次读到的就不是一个长度,而是一个值。)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;public class FileInputStreamTest {public static void main(String[] args) {FileInputStream fileInputStream=null;try {fileInputStream = new FileInputStream("E:\\Java\\JavaProjects\\javase\\IoStream\\src\\test.txt");//绝对路径,相对路径从工程下开始byte[] bytes = new byte[4];//使用一个byte数组来接收传递的数据int read = 0;//定义一个标志变量来进行判断,文件中是否还有数据while ((read=fileInputStream.read(bytes))!=-1)//判断,文件中是否还有数据,fileInputStream.read(bytes))里面存放的是该数组当次读到数据的个数,System.out.print(new String(bytes,0,read));//输出读到并放在数组中的数据} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fileInputStream!=null) {try {fileInputStream.close();//关闭流} catch (IOException e) {e.printStackTrace();}}}}
}
该txt文件被上面文件输入流读取
admin=1223525235
password=7890
FileOutputStream:
文件输出流相较文件输入流要简单一些。直接调用流的Wirter方法将数据写入,最后刷新流并且关闭流。(ture用来追加内容,不会清空原内容)
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class FileOutPutStreamTest {public static void main(String[] args) {FileOutputStream fileOutputStream=null;try {fileOutputStream = new FileOutputStream("test1.txt",true);//输出文件的路径String s = new String("made in China");byte[] b1 =s.getBytes();//将字符串转换成byte数组fileOutputStream.write(b1);//写入文件中fileOutputStream.flush();//刷新流} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fileOutputStream == null) {try {fileOutputStream.close();//关闭流} catch (IOException e) {e.printStackTrace();}}}}
}
将一个文件中的数据拷贝另一个文件中(将输入流和输出流结合):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class CopyTest {public static void main(String[] args) {FileInputStream fileInputStream=null;FileOutputStream fileOutputStream=null;try {fileInputStream = new FileInputStream("E:\\a.txt");//输入文件路径fileOutputStream = new FileOutputStream("E:\\b.txt",true);//输出文件路径,true用来追加,不清空原来存入的内容byte[] b = new byte[1024*1024];//存放读到的数据int count=0;//标志变量while ((count=fileInputStream.read(b))!=-1)//是否能读到数据fileOutputStream.write(b,0,count);//将存到数组中的数据写入到文件fileOutputStream.flush();//刷新流} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fileInputStream != null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}if (fileOutputStream != null) {try {fileOutputStream.close();//关闭流} catch (IOException e) {e.printStackTrace();}}}}
}
FileReader:
FileReader和FileInputStream类似,FileReader采用字符的方式读,FileInputStream采用字节的方式读。FileReader使用char[]数组存储读到的数据,FileInputStream使用byte[]数组存储读到的数据。
FileWirter:
和FileOutputStream用法完全一样。
ObjectOutputStream:
ObjectOutputStream用来序列化对象。将对象放到集合中,并将该集合使用对象流的writeObject()方法存放。
要想将对象序列化,则必须实现Serializable接口,这个接口只是一个标志,便于JVM识别。
如果我们一段时间后更改了序列化类的内容,则反序列化可能会出现问题,因此在反序列化时我们提前在需要序列化的类中打上序列化Id
,此后,就算我们修改了序列化类的代码内容,反序列化也可成功。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;public class SerializationTest {public static void main(String[] args) throws IOException {ArrayList<Object> objects = new ArrayList<>();//创建一个集合,存放我们new出的对象objects.add(new Serialization(1));objects.add(new Serialization(1));objects.add(new Serialization(1));objects.add(new Serialization(1));objects.add(new Serialization(1));objects.add(new Serialization(1));//指定存放路径ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("log.txt"));objectOutputStream.writeObject(objects);//将objects集合序列化,序列化后的文件内容是一堆乱码,需要反序列化使用}
}class Serialization implements Serializable{private static final long serialVersionUID = 0001L;//序列化编号,随便起编号,要具有全球唯一性private int i;public Serialization(int i) {this.i = i;}@Overridepublic String toString() {return "Serialization{" +"i=" + i +'}';}
}
ObjectInputStream:
ObjectInputStream用来反序列化对象。使用流获取到序列化文件,调用流的readObject方法,完成反序列化,并输出。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Iterator;public class SerializationreadTest {public static void main(String[] args) throws IOException, ClassNotFoundException {//把异常先抛出去//序列化文件路径ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("log.txt"));Object o = objectInputStream.readObject();//读到序列化对象,此处应该是一个集合if(o instanceof ArrayList){ArrayList a=(ArrayList)o;//向下转型for (Object aa:a) {System.out.println(aa);//增强for遍历}Iterator iterator = a.iterator();//迭代器遍历while (iterator.hasNext()) {Object next = iterator.next();System.out.println(next);}}}
}
PrintStream:
PrintStream是标准输出流,可以用来记录日志。下面的代码就是这个作用。System.setOut指定输出路径。
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;public class PrintStreamTest {public static void main(String[] args) {log("修改文字");log("修改图片");}public static void log(String s){try {PrintStream printStream = new PrintStream(new FileOutputStream("log.txt",true));System.setOut(printStream);//输出到指定文件Date date = new Date();//指定格式化后的格式SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss ssss");String format = simpleDateFormat.format(date);//格式化DateSystem.out.println(format+" "+s);} catch (FileNotFoundException e) {e.printStackTrace();}}
}
IO流配合Properties集合:
两者配合可以拿出文件中“key”对应的“value”。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;public class IoPropertiesTest throws Exception{public static void main(String[] args) { FileInputStream fileInputStream = new FileInputStream("IoStream\\src\\test.txt");//拿到文件Properties properties = new Properties();//new一个properties集合properties.load(fileInputStream);//加载文件System.out.println(properties.getProperty("admin"));//由key获得value值}}
admin=1223525235//test.txt内容
password=7890
其他流不常用。不再列举。
线程
进程是资源分配的最小单位,线程是程序执行的最小单位。一个程序就是一个进程,一个进程可以由若干个线程完成。通俗来说,进程指挥线程干活。
线程与线程之间的堆内存和方法区内存资源共享,但是栈区资源不共享。每一个线程都有一个单独的栈。
线程调度模型:
抢占式,按线程的优先级调度
均分式,给各个线程分配相同的时间片
java中线程调度涉及的方法: void setPriority(int newPriority)设置优先级。static void yield()线程让位,该方法会让线程从运行态转为就绪态。void join()将该线程合并到当前正在运行线程,相当于插队,插队的线程运行完再运行当前线程。
实现线程的三种方式:
类直接继承线程,重写线程的run方法。将此类变成线程类。
线程A和线程B同时运行。
public class ThreadExtendsTest {public static void main(String[] args) {AThread aThread = new AThread();//new一个线程对象BThread bThread = new BThread();aThread.setName("线程A");//线程重命名bThread.setName("线程B");aThread.start();//启动线程bThread.start();}}
class AThread extends Thread{//继承线程@Overridepublic void run() {//重写线程的run方法for (int i=0;i<10000;i++)//Thread.currentThread().getName()该方法为获取当前线程的名字System.out.println(Thread.currentThread().getName()+"正在运行!");}
}
class BThread extends Thread{@Overridepublic void run() {for (int i=0;i<10000;i++)System.out.println(Thread.currentThread().getName()+"正在运行!");}
}
类实现Runnable接口
public class ThreadRunnableTest {public static void main(String[] args) {CThread threadC = new CThread();//先new出对应类对象DThread threadD = new DThread();Thread cThread = new Thread(threadC);//将该对象变成线程对象Thread dThread = new Thread(threadD);cThread.setName("线程C");dThread.setName("线程D");cThread.start();dThread.start();}
}
class CThread implements Runnable{//实现Runnable接口@Overridepublic void run() {for(int i=0;i<1000;i++)System.out.println(Thread.currentThread().getName()+"正在运行!");}
}
class DThread implements Runnable{@Overridepublic void run() {for(int i=0;i<1000;i++)System.out.println(Thread.currentThread().getName()+"正在运行!");}
}
类实现Callable接口
实现Callable接口,我们会得到一个返回值,该返回值使用FutureTask.get()方法获取
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class ThreadCallableTest {public static void main(String[] args) throws ExecutionException, InterruptedException {EThread threadE = new EThread();//new一个对应类对象FThread threadF = new FThread();FutureTask futureTask1 = new FutureTask<>(threadE);//new FutureTask对象是为了获取线程返回值FutureTask futureTask2 = new FutureTask<>(threadF);Thread eThread = new Thread(futureTask1);//加入线程Thread fThread = new Thread(futureTask2);eThread.setName("E进程");fThread.setName("F进程");eThread.start();fThread.start();System.out.println(futureTask1.get());//获得返回值System.out.println(futureTask2.get());}
}class EThread implements Callable {int i=100;@Overridepublic Object call() throws Exception {return i;}
}
class FThread implements Callable {int i=200;@Overridepublic Object call() throws Exception {return i;}
}
在以后的开发环境中我们面对的都是多线程的运行环境,在这种情况下,如果多个线程对象同时修改一个共享数据,可能会造成数据安全问题。
由此引出线程同步机制:
多个线程若想同时操作一个共享数据需要排队。牺牲一部分效率,但保证了数据安全。
线程同步机制会让用户的体验降低。
解决线程同步问题:
使用局部变量代替实例变量和静态变量,局部变量在栈区,没有安全问题。
如果是实例变量的话,可以创建多个对象,让每个线程都有单独的对象可调。
如果前两种都无法解决,则只能使用synchronized。
synchronized为共享对象”加锁“
例如:
生产者和消费者共享一个仓库,只有仓库中有库存,消费者才可以消费,同样的,当仓库满后,生产者不能再生产。(使用线程同步机制后,在一个时间点上要么生产,要么消费)
wait()和notify()都是对象级的方法,sleep睡眠的是当前线程,使用引用.interrupt()打断睡眠。
import java.util.ArrayList;public class HomeWorkTest {public static void main(String[] args) {ArrayList<Object> objects = new ArrayList<>();//new一个仓库对象Producter producter = new Producter(objects);//将仓库共享给生产者Customer customer = new Customer(objects);//将仓库共享给消费者Thread threadA = new Thread(producter);//生产线程Thread threadB = new Thread(customer);//消费线程threadA.setName("生产者");threadB.setName("消费者");threadA.start();//启动threadB.start();}
}
class Producter implements Runnable{private ArrayList objects;public Producter(ArrayList objects) {this.objects = objects;}@Overridepublic void run() {while (true) {//一直生产synchronized (objects) {//为仓库加锁,锁的对象是仓库if (this.objects.size() > 0) {//当仓库中有库存时,停止生产try {this.objects.wait();//交出自己的锁,下面代码不会再执行} catch (InterruptedException e) {e.printStackTrace();}}Object o = new Object();objects.add(o);//添加库存System.out.println(Thread.currentThread().getName()+o);//输出this.objects.notify();//唤醒其他线程}}}
}class Customer implements Runnable{private ArrayList objects;public Customer(ArrayList objects) {this.objects = objects;}@Overridepublic void run() {while (true) {synchronized (objects) {if (objects.size() == 0) {//没有库存,停止消费try {this.objects.wait();//交出自己的锁,下面代码不会再执行} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + objects.get(0));//输出objects.remove(0);//移出库存objects.notify();//唤醒其他线程}}}
}
如果同步机制使用不当会造成死锁,死锁不报错误,程序也不终止。
线程A先调用o1对象,线程B先调用o2对象,当线程A去调用o2时发现o2已经被锁,当线程B去调o1时发现o1被锁。
public class DeadLockTest {public static void main(String[] args) {Object o1 = new Object();Object o2 = new Object();Thread threadA = new ThreadA(o1, o2);Thread threadB = new ThreadB(o1, o2);threadA.setName("线程A");threadB.setName("线程B");threadA.start();threadB.start();}
}class ThreadA extends Thread{private Object o1;private Object o2;public ThreadA(Object o1, Object o2) {this.o1 = o1;this.o2 = o2;}@Overridepublic void run() {synchronized (o1){//锁住o1try {Thread.sleep(5000);//设置休眠时间,使o2有大几率被B进程锁住} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){//锁住o2System.out.println(Thread.currentThread().getName()+"------正在运行");}}}
}
class ThreadB extends Thread{private Object o1;private Object o2;public ThreadB(Object o1, Object o2) {this.o1 = o1;this.o2 = o2;}@Overridepublic void run() {synchronized (o2){//锁住o2synchronized (o1){//锁住o1System.out.println(Thread.currentThread().getName()+"------正在运行");}}}
}
定时器(timer)
定时器是在特定的时间执行特定的动作,并且可以设置动作重复执行的时间。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;public class TimerTest {public static void main(String[] args) throws ParseException {Timer timer = new Timer();//new一个定时器对象TimerTask01 timerTask = new TimerTask01();//new一个定时器任务SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date firstDate = simpleDateFormat.parse("2021-11-16 11:15:00");//格式化时间timer.schedule(timerTask,firstDate,1000);//调用定时器方法(任务,执行时间,重复执行间隔)}
}
class TimerTask01 extends TimerTask {//定时器任务继承TimerTask类@Overridepublic void run() {Date time = new Date();//获取当前时间SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String format = simpleDateFormat.format(time);//格式化当前时间System.out.println(format+"完成备份");}
}
守护进程
只要我们的用户线程存在则守护线程会一直执行,一般守护线程都是一个死循环,当所有用户线程结束后,守护线程也会结束。
public class DeamonTest {public static void main(String[] args) {MyDeamon myDeamon = new MyDeamon();Thread thread = new Thread(myDeamon);thread.setDaemon(true);//设置该线程为守护线程thread.setName("守护线程");thread.start();for (int i=0;i<1000;i++)System.out.println(Thread.currentThread().getName()+"正在运行!");}
}
class MyDeamon implements Runnable{@Overridepublic void run() {while(true)System.out.println(Thread.currentThread().getName()+"正在运行!");}
}
反射机制
反射机制可以让我们直接操纵字节码文件,可以增加我们程序的扩展性。
在java中获取class的三种方式:
Class c = Class.forname("类名");//第一种
Class c = 对象.getClass();//第二种
Class C = 数据类型.class;//第三种
JDK中默认有三个类加载器:
启动类加载器(rt.jar)、扩展类加载器(ext/*.jar)、应用类加载器(classpath)。从前往后的顺序调用,其中启动和扩展被称为双亲委派机制。
反射机制和流加载路径相关问题
多种文件加载方法,起始加载路径可能不同
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.ResourceBundle;public class ReflectionTest {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {System.out.println("-----------第一种------------------");InputStream fileInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("Test.properties");//流加载法,不分操作系统,从src下开始写/*System.out.println("------------第二种-----------------");String path = Thread.currentThread().getContextClassLoader().getResource("Test.properties").getPath();//从src下开始写FileInputStream fileInputStream = new FileInputStream(path);System.out.println("----------第三种---------------");FileInputStream fileInputStream = new FileInputStream("reflection\\src\\Test.properties");//从工程下开始写*/Properties properties = new Properties();properties.load(fileInputStream);fileInputStream.close();String file = properties.getProperty("file");Class name = Class.forName(file);Object o = name.newInstance();//实例化的一个对象if (o instanceof User){User u =(User)o;u.setId(100);System.out.println(u);}}
}
流加载的文件
file=User
User类
public class User {int id;public String name;public User(int id, String name) {this.id = id;this.name = name;}public User() {}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "User{" +"id=" + id +'}';}
}
使用反射机制new对象并设置属性,获取属性。(资源绑定器是最方便的获取文件方法,但是获取的文件后缀要是properties)
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.ResourceBundle;public class ReflectionTest4 {public static void main(String[] args) throws Exception {ResourceBundle bundle = ResourceBundle.getBundle("Test");//资源绑定器绑定properties文件,路径从src下开始。不需要带properties后缀String file = bundle.getString("file");//获得文件“=”号右侧与之对应的内容Class forName = Class.forName(file);//获取类,如果直接写相对路径的话也是从src下开始,一直写到类,中间用“.”连接Object obj = forName.newInstance();//new实例Field id = forName.getDeclaredField("id");//得到id属性id.set(obj,1001);//赋值Field name = forName.getDeclaredField("name");name.setAccessible(true);//破除封装name.set(obj,"张三");System.out.println(id.get(obj)+" "+name.get(obj));}
}
使用反射机制new对象并调用实例方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class ReflectionTest5 {public static void main(String[] args) throws Exception {Class methodloginTest = Class.forName("MethodloginTest");Object obj = methodloginTest.newInstance();Method method = methodloginTest.getDeclaredMethod("login", String.class, String.class);//得到方法Object o = method.invoke(obj, "1001", "password");//调用方法,传初值(invoke是调用)if (o instanceof Boolean){boolean oo = (boolean)o;System.out.println((oo?"登录成功":"登陆失败"));}Method loginout = methodloginTest.getDeclaredMethod("loginout");loginout.invoke(obj);//调用方法}
}class MethodloginTest {public boolean login(String admin,String password){if (admin.equals("1001")&&password.equals("password"))return true;elsereturn false;}public void loginout(){System.out.println("退出成功!");}
}
调用newInstance方法创建对象时自动调用该类的无参构造方法。
import java.lang.reflect.Constructor;public class ReflectionTest7 {public static void main(String[] args) throws Exception{Class user = Class.forName("User");Object obj = user.newInstance();//自动调用无参构造Constructor constructor = user.getDeclaredConstructor(int.class, String.class);Object newInstance = constructor.newInstance(123, "abc");//使用有参构造创建对象System.out.println(newInstance);}
}
注解
注解(Annotation)是一种引用数据类型,编译之后生成class文件。
注解可以出现在,类、方法、变量、属性上。
定义注解:
[修饰符列表] @interface 注解名{
数据类型 变量名1(); 数据类型可以是基本类型,也可以基本数据类型的数组,还可以是类和String和String数组
数据类型 变量名2();
}
如果注解的变量名是“value”并且该注解只有一个属性则使用注解时不需要”value=值“,只需要“值”即可。数组中如果只有一个值的话”{}“可以省略。
元注解:
常用的有:Target、Retention。其中Target用来设置该注解可以注解的目标。Retention用来保存注解最后保存的位置。(保存位置一般为source或class或runtime(runtime属性可以让我们使用反射机制))
常用注解:Override、deprecated。
被Override注解修饰的方法需要重写父类的方法。
被deprecated注解修饰的类或方法或其他。。。会显示已过时。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)//该注解只能放到类上
public @interface MyAnnotationClass {int name1();String name2();//注解中可以有属性
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//该注解只能放到属性上
@interface MyAnnotationField{int value() default 123;//value属性赋值时只需要在注解中写值就可以了//String[] s();数组中只有一个值的话不用加大括号
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)//该注解只能放到构造方法上
@interface MyAnnotationConstructor{}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)//该注解只能放到方法上
@interface MyAnnotationMethod{}
使用注解、获取自定义注解值
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;@MyAnnotationClass(name1 = 123,name2="admin")//使用注解,要给注解中的属性赋值
public class AnnotationTest1 {//测试public static void main(String[] args) throws Exception{Class forName = Class.forName("AnnotationTest1");boolean ClassAnnotationPresent = forName.isAnnotationPresent(MyAnnotationClass.class);//判断该类是否有对应注解if(ClassAnnotationPresent){Annotation classAnnotation = forName.getAnnotation(MyAnnotationClass.class);//获取类注解值System.out.println(classAnnotation);}Field id = forName.getDeclaredField("id");boolean idAnnotationPresent = id.isAnnotationPresent(MyAnnotationField.class);//判断该属性是否有对应注解if(idAnnotationPresent){MyAnnotationField idAnnotation = id.getAnnotation(MyAnnotationField.class);//获取属性注解值System.out.println(idAnnotation);}Method dosome = forName.getDeclaredMethod("dosome");boolean dosomeAnnotationPresent = dosome.isAnnotationPresent(MyAnnotationMethod.class);//判断该方法是否有对应注解if (dosomeAnnotationPresent){MyAnnotationMethod dosomeAnnotation = dosome.getAnnotation(MyAnnotationMethod.class);//获取方法注解值System.out.println(dosome);}}@MyAnnotationField(10001)private int id;private String name;@MyAnnotationConstructorpublic AnnotationTest1(int id, String name) {this.id = id;this.name = name;}@MyAnnotationMethodpublic void dosome(){System.out.println("方法被调用!");}
}
javase 学习笔记相关推荐
- 【JavaSE学习笔记】
JavaSE学习笔记 一.java的基本语法 变量运算规则 编码情况1中l后面没有加L,默认是int变量 编码情况2中b1=b+1中的1默认是int型变量,会出错 string类型 string里面可 ...
- JavaSE学习笔记(持续更新)
这里写目录标题 JavaSE学习笔记(持续更新) Java跨平台原理与核心机制 1.跨平台原理: 2.两种核心机制: JDK11的安装流程 Java程序开发的三个步骤(无编辑器版) Eclipse安装 ...
- 重拾JavaSE学习笔记
重拾JavaSE学习笔记 1.常用DOS命令 2.了解Java 2.1 .java特性 2.2.JDK .JRE.JVM 2.3.java的加载和执行 3.开发环境搭建 3.1.安装jdk 3.2.配 ...
- javaSE学习笔记01 入门篇
javaSE学习笔记01 入门篇 java语言概述 Java背景知识 java是 美国 sun 公司 在1995年推出的一门计算机高级编程语言. java早期称为Oak(橡树),后期改名为Java. ...
- 我的javaSE学习笔记
layout: post title: "我的JAVASE自学笔记" date: 2019-05-18 20:23:25 +0800 我的JAVASE自学笔记 作者:吴甜甜 个人博 ...
- JavaSE学习笔记-Day1
笔者是一名大二在读本科生,最近闲着无聊重拾起Java这门语言,看了些许教学视频后居然还觉得挺有意思,"情不知所起,一往而深".于是决心认真学习这门语言!由于身居科班,自然不是零基础 ...
- JavaSE学习笔记(一)基础知识
本章包含内容有: java环境配置.注释.标识符.数据类型.类型转换.变量.常量.运算符.包机制.顺序结构.选择结构.循环结构.方法的定义和调用.命令行传参.可变参数. 点击这里查看更多JavaSE的 ...
- javaSE学习笔记——第十四天正则表达式、Math类、System类、Data类、Calendar类等
javaSE学习第十四天 java知识 正则表达式的概述和简单使用 A:正则表达式 是指一个用来描述或者匹配一系列符合某个语法规则的字符串的单个字符串.其实就是一种规则.有自己特殊的应用. 作用:比如 ...
- javaSE学习笔记——第四天循环结构、方法等
javaSE学习第四天 java知识 循环结构的分类 for,while,do-while 循环结构for语句的格式: for(初始化表达式;条件表达式;循环后的操作表达式) {循环体;} for语句 ...
- 刘意JavaSE 学习笔记——总纲
开始学习JavaSE了,记录下自己学习的进程. 学习大纲 1 开发环境配置 day01 配置 2 基础语法(基本差不多,看一下就好) day02 命名规则,进制转换 day03 运算符,if语句 da ...
最新文章
- MAXIEYE创始人周圣砚:以规模化迎接智能驾驶科技平权时代 | MEET2022
- java Concurrent包学习笔记(一):ExecutorService
- Pro*c使用指示变量来处理NULL列值
- 求有向图的简单路径_2020福建农信社招聘-关键路径
- 使用Mysql 5.5数据库Hibernate自动建表创建表出错table doesn't exist
- SQLServer2008数据库还原失败 恢复失败
- 32位/64位机上常用数据类型字节数(C语言)
- 金蝶k3 与用友ncc凭证同步
- 使用 OpenSSL 创建ssl自签名证书
- MySQL - redolog 图文详解
- java 实现橡皮擦_基于canvas剪辑区域功能实现橡皮擦效果
- vue中img本地图片地址的具体使用
- FR-报表应用-分组报表-动态显示N个分组
- 第三集 怪物学院 第十七章
- 群晖docker位置_OMV利用Docker配置nextcloud,实现个人网盘的搭建!
- Android 应用(8)——使用Ubuntu制作APP签名文件并对应用签名
- css箭头图片方向转换
- 服务器运行Microsoft.Office.Interop.Word出错{00024500-0000-0000-C000-000000000046}问题总结
- 为什么溺水事故无法“清零”?
- 大恒工业相机实例使用
热门文章
- 停车场进出库系统(链表和队列)链表要按照VIP等级从大到小(简易版)
- 如何使用手机访问电脑上的视频
- 黑色高端壁纸小程序,界面简洁高端,主要有壁纸专辑,头像,电脑平板壁纸,手机壁纸
- 你还在一天三充?华为手机开启这个隐藏省电功能,两天一充很简单
- 70行python代码爬取新浪财经中股票历史成交明细
- 使用html+css实现一个静态页面(化妆品公司 5页_含源码)
- Ubuntu20.04 打字键盘没反应,光标处无输出
- 最适合小白的matlab教程系列_进阶系列二之多项式
- Skin++皮肤控件的使用
- js判断字符串包含某个字符串的多种方法