目录
alt + insert
IDEA新建代码,在代码中使用可以生成构造方法,tostring方法等。
Java 语言的软件开发工具包(Java Development Kit,简称JDK):它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。
Java运行环境(Java Runtime Environment,简称JRE)是一个软件,JRE可以让计算机系统运行Java应用程序。
没有JDK的话,无法编译Java程序(指java源码.java文件),如果想只运行Java程序(指class或jar或其它归档文件),要确保已安装相应的JRE。
javac.exe
命令负责编译,java.exe
命令负责运行
byte+short+char 做混合运算时,会各自先转换成int再做运算
除此之外,其它多种数据类型做混合运算的时候,最终的结果类型是“最大容量”对应的类型
public class test
{
public static void main (String[] args)
{
char c = 'a';
byte b = 1;
short s = 2;
long l = 10L;
(c + b + s) // int类型
(b + c) // int类型
(c + b + s + l) // long类型
(c + l) // long类型
}
}
switch 只支持 int 和 String 类型,如果case无break会出现穿透现象
case合并:
public class switchTest {
public static void main(String[] args) {
java.util.Scanner s = new java.util.Scanner(System.in);
int num = s.nextInt;
switch(num) {
case 1: case 2: case 3:
System.out.println("aaa");
break;
case 4:
System.out.println("bbb");
break;
default:
System.out.println("ccc");
}
}
}
调用方法:类名.方法名(其中在一个类中调用,类名可以省略)
方法重载条件:
- 在一个类中
- 方法名相同
- 参数列表不同(至少满足以下一个条件即可)
- 参数个数不同 eg(int a), eg(int a, int b)
- 参数类型不同 eg(int a), eg(double d)
- 参数顺序不同 eg(int a, double d), eg(double d, int a)
以下两种情况是方法重复,并非方法重载
/*返回值类型不同*/
public static int sum() {
return 1;
}
public static double sum() {
return 1.0;
}
/*修饰符类型不同*/
public static void w1() {
}
void w1() {
}
面向对象三大特征:
- 封装
- 继承
- 多态
类 ---> [实例化] ---> 对象(实例)
对象 ---> [抽象] ---> 类
方法体内的变量是局部变量,局部变量需手动赋值
方法体外,类体内的变量是成员变量,可不手动赋值,未手动则自动赋默认值
实例化对象在栈中存储的是地址,这个地址是堆内存对象的内存地址
基本数据类型在栈中存储的是数据值
空指针异常:空引用访问" 实例相关的数据 (实例变量/实例方法) "
构造方法
作用:1、创建对象 2、给属性赋值
public class test {
int num;
String str;
boolean age;
public test() {
//若类中无任何构造方法,则系统会默认创建一个无参构造方法,称为缺省构造器
}
public test(int a, String b, boolean c) {
num = a;
str = b;
age = c;
}
}
public class constructor {
public static void main(String[] args) {
test t1 = new test(); //通过new调用构造方法,也是在此时候初始化实例变量
System.out.println(t1.num);
System.out.println(t1.str);
System.out.println(t1.age);
//0 null false
test t2 = new test(111, "aaa", true);
System.out.println(t1.num);
System.out.println(t1.str);
System.out.println(t1.age);
//111 aaa true
System.out.println(t1); //test@16d3586
System.out.println(t2); //test@154617c
}
}
封装
第一步:代码私有化
第二步:一个属性对外提供两个get方法和set方法
不带static的方法被称为实例方法,实例方法调用时必须先new对象
get:
public 返回值类型 get+参数首字母大写(无参) {
return xxx;
}
set:
public void set+参数首字母大写(一个参数) {
xxx = 一个参数;
}
public class test {
//以下实例的,都是与对象相关的,访问时采用“引用.”的方式,需要先new对象
int x1; // 成员变量中的实例变量,在new对象的时候初始化,存储在堆中
public void m1() { // 实例方法
int y1; // 局部变量,存储在栈中
}
//以下静态的,都是与类相关的,访问时采用“类名.”的方式,不需要new对象
static int x2; //成员变量中的静态变量,在类加载的时候初始化,存储在方法区中
public static void m2() { //静态方法
int y2; // 局部变量,存储在栈中
}
}
静态代码块与实例代码块
public class test {
static { // 静态代码块,在类加载时调用
System.out.print("A");
}
{ // 实例代码块,在构造方法执行之前调用
System.out.print("B");
}
public static void main(String[] args) {
System.out.print("C");
new test();
System.out.print("D");
}
public test() { // 构造方法,在new对象时调用
System.out.print("E");
}
static {
System.out.print("F");
}
}
//AFCBED
this
- this保存当前对象的内存地址,指向自身。
- this存储在堆内存中对象的内部。
- this不能使用在静态方法中,可以使用在实例方法和构造方法中,如果是在构造方法中使用,可以通过当前的构造方法去调用另一个本类的构造方法
//构造方法中使用this
public class test {
private int year;
private int month;
public test() {
/* this.year = year;
this.month = month;*/
this(2022, 9); //代码复用,只能在构造方法的第一行
}
public test(int year, int month) {
this.year = year;
this.month = month;
}
}
- 一般情况下this可以省略 ,某些情况比如区分局部变量和实例变量时this不能省略
//以下情况this不能省略
public class test {
private int no;
public test(int no) {
this.no = no;
}
public void setNo(int no) {
this.no = no;
}
}
extends(继承)
-
基本作用:代码可以得到复用。
-
重要作用:有了继承关系,才有了后期的方法覆盖和多态机制。
-
子类继承父类,除构造方法不能继承外,剩下都可以继承,但是私有的属性无法在子类中直接访问(可以通过间接方法比如set和get)
方法覆盖
子类继承父类之后当继承过来的方法不满足需求,子类可以将该方法重新编写,称为“方法覆盖”。
-
条件一:两个类必须有继承关系
-
条件二:方法的返回值类型、方法名、形式参数列表要和被覆盖的方法一模一样
-
条件三:访问权限不能更低,可以更高(比如不能protected覆盖public)
-
条件四:重写之后的方法不能比之前的方法抛出更多的异常,可以更少
注意
-
注意一:私有方法无法覆盖
-
注意二:方法覆盖只是针对于实例方法,静态方法覆盖没有意义
class Dad {
public void way() {
System.out.println("this is dad");
}
}
class Son extends Dad {
public void way() {
System.out.println("this is son");
}
}
public class test {
public static void main(String[] args) {
Son s = new Son();
s.way(); // this is son
}
}
多态
- 父类型的引用指向子类型的对象
- 多种形态(编译形态和运行形态)
- 编译阶段:静态绑定父类的方法
- 运行阶段:动态绑定子类型对象的方法
- 在开发中的作用:降低程序的耦合度,提高程序扩展力
向上转型和向下转型
前提:两者之间必须有继承关系
向上转型
子 --> 父(自动类型转换)
animal a = new cat();
向下转型(为了调用或者执行子类中特有的方法)
父 --> 子(需要添加强制类型转换符)
cat c = (cat)a;
为了规避ClassCastExceptin
风险,进行向下转型之前先用instanceof 运算符进行判断
public class test {
public void test1(animal a) {
if(a instanceof cat) {
cat c = (cat)a;
c.catchMouse();
}
else if(a instanceof bird) {
bird b = (bird)a;
b.sing();
}
}
}
instanceof 运算符
语法:引用 instanceof 类型
作用:可以在运行的时候动态的判断引用指向的对象的类型
animal a = new cat();
System.out.println(a instanceof animal); //true
System.out.println(a instanceof cat); //true
System.out.println(a instanceof bird); //flase
super
-
super不能使用在静态方法中,可以使用在实例方法和构造方法中
-
语法:
super.
、super()
super.属性名
【访问父类型的属性】super.方法名
【访问父类型的方法】super(实参)
【调用父类型的构造方法】
-
super.
大部分情况可以省略,当父类和子类中有同名的属性或者相同方法,要在子类中访问父类的属性或者方法时,super不可省略class customer{ String name; public customer(){} public customer(String name){ this.name = name; } public void way(){ System.out.println("父类方法调用了"); } } class VIP extends customer{ String name; public VIP(){} public VIP(String name){ super(name); } public void way(){ System.out.println("子类方法调用了"); } public void dosome(){ System.out.println(this.name + "is doing something..."); //null is doing something... System.out.println(super.name + "is doing something..."); //zazahui is doing something... System.out.println(name + "is doing something..."); //null is doing something... this.way(); //子类方法调用了 super.way(); //父类方法调用了 way(); //子类方法调用了 } } public class test{ public static void main(String[] args){ VIP v = new VIP("zazahui"); v.dosome(); } }
-
super()
只能出现在构造方法的第一行,通过当前的构造方法去调用父类中的构造方法。目的:创建子类对象时,先初始化父类型特征 -
当构造方法的第一行既没有
this()
也没有super()
时,默认会有一个super()
public class test {
public static void main(String[] args) {
new c();
//1 3 6 5 4
}
}
class a{
public a(){
System.out.println("1: a的无参构造方法")
}
}
class b extends a{
public b(){
System.out.println("2: b的无参构造方法")
}
public b(String name){
//super();
System.out.println("3: b的有参构造方法(String)")
}
}
class c extends b{
public c(){
this("Simho");
System.out.println("4: c的无参构造方法")
}
public c(String name){
this(111, name);
System.out.println("5: c的有参构造方法(String)")
}
public c(int id, String name){
super(name);
System.out.println("6: c的有参构造方法(int, String)");
}
}
-
super不是引用,不保存内存地址,也不指向任何对象,只是代表当前对象的父类型特征,所以不能单独使用。
final
-
最终的,不可变的
-
final修饰的类——无法被继承
-
final修饰的方法——无法被覆盖、重写
-
final修饰的变量
-
final修饰的局部变量——无法进行二次赋值
public class test { public static void main(String[] args) { final int k = 100; int k = 200; //不允许 } }
-
final修饰的"引用"变量——无法二次创建并指向新对象(即内存地址不可改变);对象内部的属性可以改变
public class test { public static void main(String[] args) { final A a = new A(); a = new A(); //不允许 a = null; //也不允许 } } class A { int age; }
-
final修饰的实例变量——除了无法进行二次赋值,还必须要手动赋初始值,系统不会对其赋默认值
public class test { final int a = 10; } or public class test { final int a; public test() { this.a = 10; } }
final修饰的实例变量——一般在final前面添加static以节省内存空间,static final联合修饰的变量称为“常量”
- 常量和静态变量一样,都是存储在方法区,且在类加载时初始化
- 区别在于常量的值无法改变,一般都是公开的
public class Chinese { String id; String name; public static final String COUNTRY = "中国"; }
-
abstract(抽象类)
-
类和类之间具有共同特征,将这些特征提取出来,形成的类就是抽象类。
-
由于类不是真实存在的,因此抽象类无法实例化、无法创建对象,所以抽象类是用来被子类继承的,因此
final
和abstract
不能联合使用。 -
抽象类虽然无法实例化,但是有构造方法,其构造方法是给子类使用的。
-
抽象类属于引用数据类型。
-
抽象类的子类可以是抽象类。
-
抽象类中不一定有抽象方法,但是抽象方法一定要在抽象类中。
-
若非抽象类继承抽象类,必须要将抽象类中的抽象方法全部实现(重写/覆盖)。
abstract class Account { public abstract void dosome(); //抽象方法 } class CreditAccount extends Account { public void dosome(){} }
java语言中凡是没有方法体的方法都是抽象方法(错误) //Object类中就有很多没有方法体的方法
interface(接口)
-
接口属于引用数据类型。
-
接口支持多继承,一个类可以实现多个接口。
interface A {} interface B {} interface C extends A,B {} abstract class D implements A,B,C{} class E implements A,B,C {}
-
接口是完全抽象的。
-
接口中所有的元素都是
public
修饰的。 -
接口中只有常量+抽象方法(所以接口中不能有方法体);其中常量中的
public static final
和 抽象方法中的public abstract
可以省略。interface Animal { String type = "animal"; // 等价于public static final String type = "animal"; void move(); // 等价于public abstract void move(); }
-
类与类、接口与接口之间叫做继承,类和接口之间叫做实现。
继承用
extends
关键字完成,实现用implements
关键字完成。 -
若非抽象类实现接口,必须将接口中的抽象方法全部实现(重写/覆盖)。
-
若
extends
和implements
同时出现,先extends
后implements
。 -
接口可以使用多态。
-
在开发中的作用:面向接口编程,可以降低程序的耦合度,提高程序的扩展力。
访问控制权限
权限大小:public > protected > default > private
访问控制修饰符 | 本类 | 同包 | 子类 | 任意位置 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
异常
- 编译时异常——必须处理,不然编译报错程序无法运行
- 运行时异常——可选择处理或者不处理
处理异常的两种方式
-
在方法声明的位置上使用
throws
关键字抛出,谁调用此方法就抛给谁。进行上报处理,后续代码不会执行。 -
使用
try..catch
语句对异常进行捕捉。自行处理消化,语句后续代码仍会执行。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class exceptionTest {
public static void main(String[] args) {
try {
m1();
System.out.println("若m1()出现异常,此语句不会执行");
} catch (FileNotFoundException e) {
System.out.println("文件路径可能存在错误!");
}
System.out.println("异常被处理,此语句可以正常执行");
}
public static void m1() throws FileNotFoundException { //异常上抛给main
System.out.println("m2 begin");
m2();
System.out.println("m2 end"); //不会执行
}
public static void m2() throws FileNotFoundException { //异常上抛给m1
System.out.println("m3 begin");
m3();
System.out.println("m3 end"); //不会执行
}
public static void m3() throws FileNotFoundException { //异常上抛给m2
new FileInputStream("c://一个不存在的路径");
}
}
/*
m2 begin
m3 begin
文件路径可能存在错误!
异常被处理,此语句可以正常执行
*/
-
异常处理机制的作用:增强程序的健壮性(异常发生了也不影响程序的执行)。
-
catch()括号中的类型和throws关键字后的类型,可以填写具体的异常类型,也可以填写该异常类型的父类型。
-
若异常有多个,可以写多个catch对应不同的异常一个个的处理,但是需要遵循从上到下、从小到大的原则(写了父类型后面就不能跟它的子类型)。
finally子句
-
可以不加,但必须同try一起使用,finally中的语句最后执行,也一定会执行。
-
通常在finally语句块中完成资源的释放/关闭。
实现多线程的方式
-
第一种:编写一个类,直接继承
java.lang.Thread
,重写run方法public class threadTest { public static void main(String[] args) { myThread t = new myThread(); t.start(); // 启动一个分支线程,在JVM中开辟一个新的栈空间。启动成功的线程会自动调用run方法 for(int i=0; i < 1000; i++) { System.out.println("主线程-->" + i); } } } class myThread extends Thread { public void run() { for(int i=0; i < 1000; i++) { System.out.println("分支线程-->" + i); } } }
-
第二种:编写一个类,实现
java.lang.Runnable
接口,实现run方法public class threadTest { public static void main(String[] args) { myRunnable mr = new myRunnable(); Thread t = new Thread(mr); //或者合并写成 Thread t = new Thread(new myRunnable()); 推荐这种 t.start(); for(int i=0; i < 1000; i++) { System.out.println("主线程-->" + i); } } } class myRunnable implements Runnable { public void run() { for(int i=0; i < 1000; i++) { System.out.println("分支线程-->" + i); } } }
-
基于第二种,还可以使用匿名内部类
public class threadTest { public static void main(String[] args) { Thread t = new Thread(new Runnable() { // 相当于new [匿名] implements Runnable() {} public void run() { for(int i=0; i < 1000; i++) { System.out.println("分支线程-->" + i); } } }); t.start(); for(int i=0; i < 1000; i++) { System.out.println("主线程-->" + i); } } }
数组
- 数组非基本数据类型,是一种引用数据类型
- 数组中的元素类型必须统一,例如
int
类型的数组只能存储int
类型,Person
类型的数组只能存储Person
类型 - 数组中首个元素的内存地址作为整个数组对象的内存地址,数组中元素的内存地址是连续的
初始化一维数组
public class test {
public static void main(String[] args) {
// 静态初始化语法格式
int[] num = {1, 2, 3, 4};
String[] str = {"a", "b", "c"};
Person per1 = new Person();
Person per2 = new Person();
Person[] p = {per1, per2};
// 动态初始化语法格式
int[] num = new int[4];
String[] str = new String [3];
Person[] p = new Person[2];
P[0] = new Person();
P[1] = new Person();
}
}
其他初始化方式
public class test {
public static void main(String[] args) {
printArray(new int[]{1,2,3}); // 静态初始化
// 1 2 3
printArray(new int[3]); // 动态初始化
// 0 0 0
}
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) { // length是数组的一个默认方法,用于表示数组元素的个数
System.out.println(array[i]);
}
}
}
数组拷贝 / 扩容
System.arraycopy(拷贝源, 源起始位置, 拷贝目标, 目标起始位置, 拷贝长度)
public class test {
public static void main(String[] args) {
int[] src = {1, 2, 3, 4};
int[] dest = new int[10];
System.arraycopy(src, 0, dest, 3, src.length);
for (int i = 0; i < dest.length; i++) {
System.out.println(dest[i]); // 0 0 0 1 2 3 4 0 0 0
}
}
}
初始化二维数组
public class test {
public static void main(String[] args) {
int[][] a = { // 静态初始化
{1,2,3},
{4,4,4,5,5},
{7}
};
int[][] b = new int[3][4]; // 动态初始化
}
}
其他初始化方式和一维同理
IO流
所有的流都位于java.io.*
下
文件流:
*java.io.FileInputStream // 字节输入流
*java.io.FileOutputStream // 字节输出流
java.io.FileReader // 字符输入流
java.io.FileWriter // 字符输出流
转换流(将字节流转换成字符流):
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流://不用自己开数组
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
// 在java中只要"类名"以Stream结尾都是字节流,以Reader/Writer结尾都是字符流
所有的流都有close
方法,用完流需要关闭管道以释放资源。
所有的输出流都有flush
方法,最终输出后需要flush刷新以清空管道。
FileInputStream & FileReader(文件读取)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class test {
public static void main(String[] args) {
FileInputStream fis = null; // 可以读取任意文件
FileReader fr = null; // 只能读取普通文本
try {
fis = new FileInputStream("E:\\IDEA_greenEditon\\IdeaProjects\\course\\a");
fr = new FileReader("E:\\IDEA_greenEditon\\IdeaProjects\\course\\a");
byte[] bytes = new byte[4]; // 以byte数组为单位,一次读取bytes.length长度的字节
char[] chars = new char[4]; // 以char数组为单位,一次读取chars.length长度的字符
int readcount = 0;
int readcount1 = 0;
while ((readcount = fis.read(bytes)) != -1) { // fis.read(bytes)返回每次读取字节的长度,为-1则表示读取完毕
System.out.print(new String(bytes, 0, readcount)); // 输出每次读取长度的字节
}
while ((readcount1 = fr.read(chars)) != -1) { // fis.read(bytes)返回每次读取字符的长度,为-1则表示读取完毕
System.out.print(new String(chars, 0, readcount1)); // 输出每次读取长度的字符
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileOutputStream & FileWriter(文件写入)
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
public class test {
public static void main(String[] args) {
FileOutputStream fos = null; // 可以写入任意文件
FileWriter fw = null; // 只能写入普通文本
try {
// fos = new FileOutputStream("E:\\IDEA_greenEditon\\IdeaProjects\\course\\a"); // 覆盖写入
fos = new FileOutputStream("E:\\IDEA_greenEditon\\IdeaProjects\\course\\a", true); // 追加写入
// fw = new FileWriter("E:\\IDEA_greenEditon\\IdeaProjects\\course\\a"); // 覆盖写入
fw = new FileWriter("E:\\IDEA_greenEditon\\IdeaProjects\\course\\a", true); // 追加写入
byte[] bytes = {97, 98, 99, 100};
fos.write(bytes); // 写入abcd
// 写入字符串时要先转成字节
String s = "你好";
byte[] bs = s.getBytes();
fos.write(bs);
char[] chars = {'你', '好', '朋', '友'};
fw.write(chars,1,3); // 第三个参数表示写入长度,写入"好朋友"
fw.write("哈哈哈");
fos.flush(); // 写完后刷新管道,以防后续数据丢失
fw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
同时进行文件的读取和写入便可实现复制功能
BufferedReader
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
public class test {
public static void main(String[] args) throws Exception{ // 为方便此处将所有异常做上抛处理
FileReader fr = new FileReader("a");
// 当一个流的构造方法需要另一个流的时候,被传进来的流叫做节点流,外部负责包装的流叫做包装流/处理流
//节点流和包装流是相对的,此时FileReader为节点流,BufferedReader为包装流
BufferedReader br = new BufferedReader(fr);
//缓冲流只能传入字符流,字节流需要先用转换流转成字符流
FileInputStream fis = new FileInputStream("a");
InputStreamReader reader = new InputStreamReader(fis);
BufferedReader br1 = new BufferedReader(reader);
// 可以合并成以下形式
// BufferedReader br1 = new BufferedReader(new InputStreamReader(new FileInputStream("a")));
String str = null;
while ((str = br1.readLine()) != null) {
System.out.println(str);
}
br.close();
br1.close();
}
}
其他几个缓冲流同理,不再赘述
Properties
-
Properties是一个Map集合,继承Hashtable,Properties的
key
和value
都是String类型 -
数据的插入和读取
Properties pro = new Properties(); pro.setProperty("k1","v1"); String value = pro.getProperty("k1");
-
利用
IO
和Properties
创建对象String key = "b"; Properties pro = new Properties(); pro.load(new FileInputStream("a.properties")); // a.properties文件存放了 b=java.util.String String className = pro.getProperty(key); Class c = Class.forName(className); Object o = c.newInstance();
序列化与反序列化
- 参与序列化与反序列化的对象,必须要实现serializable接口
- serializable接口是一个标志接口,起到标识作用,java虚拟机会根据此接口为该类自动生成一个序列化版本号
- 序列化版本号可以标识每个类,反序列化的时候便以区分
- 建议将序列化版本号自行手动写出来
// user类
import java.io.Serializable;
public class user implements Serializable {
private int no;
private String name;
//transient关键字表示游离的,不参与序列化
private transient int age; // age属性不参与序列化
private static final long serialVersionUID = 1L; //手动设置序列化版本号
public user(int no, String name, int age) {
this.no = no;
this.name = name;
this.age = age;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写tostring方法
@Override
public String toString() {
return "user{" +
"no=" + no +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//序列化操作
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class test {
public static void main(String[] args) throws Exception{
user u = new user(111,"admin",18);
ObjectOutputStream oop = new ObjectOutputStream(new FileOutputStream("users"));
oop.writeObject(u);
oop.flush();
oop.close();
}
}
// 会在根目录生成users字节码文件
//反序列化操作
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class test1 {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
Object obj = ois.readObject();
System.out.println(obj);
ois.close();
}
}
// user{no=111, name='admin', age=0}
反射
-
反射:通过反射机制可以操作字节码文件
-
作用:使程序更加灵活
-
反射机制相关的重要的类
java.lang.class java.lang.reflect.Field //重点 java.lang.reflect.Method //重重点 java.lang.reflect.Constructor
-
获取java.lang.class实例
// 第一种:Class c = Class.forName("完整包名+类名") Class c1 = null; c1 = Class.forName("java.lang.String"); // c1代表String.class文件,或者说c1代表String类型 // 第二种:Class c = 对象.getClass() String s = "aaa"; Class c2 = s.getClass(); // 第三种:Class c = 类型.class Class c3 = String.class; Class d = int.class; // d代表int类型 System.out.println(c1 == c2); // true(==判断的是对象的内存地址) System.out.println(c1 == c3); // true
-
获取class后,可以调用无参构造方法来实例化对象
c = Class.forName("test.user"); Object o = c.newInstance(); //newInstance方法调用的是该类的无参构造方法 //如果没有无参构造方法会出现“实例化”异常 Class.forName("test.user"); //如果单独使用Class.forName会进行类的加载,类加载时执行静态代码块
-
Field
public class test1 { public static void main(String[] args) throws Exception{ Class c1 = null; c1 = Class.forName("test1.user"); // c1代表String.class文件,或者说c1代表String类型 Object o1 = c1.newInstance(); // 获取对象 // Filed: Field f1 = c1.getDeclaredField("no"); //获取单个属性 f1.setAccessible(true); // 打破封装,使其可以访问private属性 // get和set: f1.set(o1,111); // 为o1对象的f1属性赋值 System.out.println(f1.get(o1)); // 访问o1对象的f1属性 Field[] fields = c1.getDeclaredFields(); // 获取所有属性,数组的每个元素存储属性对象 for(Field field : fields) { // 获取属性修饰符列表: int i = field.getModifiers(); // 属性修饰符以int类型返回 String fieldModifier = Modifier.toString(i); // 将int类型转为对应修饰符字符串 // 获取属性类型: // Class type = field.getType(); // 返回值为class类型 // String fieldType = fieldType.getName(); //.getSimpleName() String fieldType = field.getType().getSimpleName(); // 以上两条可以合并成一句;也有.getSimpleName() // 获取属性名: String fieldName = field.getName(); System.out.println(fieldModifier + " " + fieldType + " " +fieldName); } } }
-
Method
public class test1 { public static void main(String[] args) throws Exception{ Class c1 = null; c1 = Class.forName("test1.user"); // c1代表String.class文件,或者说c1代表String类型 Object o1 = c1.newInstance(); // 获取对象 //Method: Method[] methods = c1.getDeclaredMethods(); for(Method method : methods) { StringBuilder s = new StringBuilder(); // 获取方法修饰符列表: String methondModifier = Modifier.toString(method.getModifiers()); // 获取方法类型: String methodType = method.getReturnType().getSimpleName(); // 获取方法名: String methodName = method.getName(); s.append(methondModifier+" "+methodType+" "+methodName+" ("); // 获取方法参数列表 Class[] parameters = method.getParameterTypes(); for(Class parameter : parameters) { String methodParameter = parameter.getSimpleName(); s.append(methodParameter+","); } if (parameters.length > 0) { s.deleteCharAt(s.length()-1); // 删除最后一个字符 } s.append(")"); // System.out.println(s); } Method m1 = c1.getDeclaredMethod("login", String.class, int.class); // 获取c1类的参数列表为String、int的b方法 Object retValue = m1.invoke(o1, "admin", 123456); // o1对象调用该方法 } }
-
Constructor(说一下调用无参和有参构造方法,其他的和Method相似)
Constructor con1 = c1.getDeclaredConstructor(); Object ob1 = con1.newInstance(); // 同c1.newInstance()一样,后者在jdk9之后的版本不再建议使用 Constructor con2 = c1.getDeclaredConstructor(int.class, String.class, int.class); Object ob2 = con2.newInstance(1, "admin", 18);
-
获取父类和父接口
// 获取父类 Class superClass = c1.getSuperclass(); String scName = superClass.getName(); System.out.println(scName); // 获取父接口 Class[] interfaces = c1.getInterfaces(); for(Class in : interfaces) { System.out.println(in.getName()); }
可变长度参数
- 语法:类型...
- 可变长参数的个数为0~N个
- 可变长度参数必须在参数列表的最后一个位置上,并且只能有一个可变长参数
- 可变长参数可看作是一个数组
public class test {
public static void main(String[] args) {
m(1);
System.out.println("===========");
m(2,"aa");
System.out.println("===========");
m(3,"bb","cc");
}
public static void m(int a, String... str) {
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
}
}
/*
===========
aa
===========
bb
cc
*/
通用径获取方式:
- 前提:文件必须在类路径下,也就是src路径下(src是类的根路径)
// 假如src的test文件夹下有个a.txt文件
String path = Thread.currentThread().getContextClassLoader().getResource("test/a.txt").getPath();
System.out.println(path);
// /E:/IDEA_greenEditon/IdeaProjects/course/out/production/course/test/a.txt
// 直接以流的形式返回
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("test/a.txt");
注解(Annotation)
-
自定义注解的语法格式
[修饰符列表] @interface 注解类型名 { } // myAnnotation.java public @interface myAnnotation { int age(); String[] name(); } // test.java public class test { @myAnnotation(age = 18, name = {"admin", "root"}) // 当数组只有一个元素时,花括号可以省略 public static void main(String[] args) { } }
-
JDK内置的常用注解
Deprecated:用来表明这个注解标注的元素已过时 Override:用来检查是否重写了父类的方法;该注解只能出现在方法上
-
元注解:用来标注
注解类型
的"注解
"-
常用的元注解
Target:用来标注"被标注的注解"可以出现在哪些位置上 例如:@Target(ElementType.METHOD) 表名该注解只能出现在方法上 Retention:用来标注"被标注的注解"最终保存在哪里 @Retention(RetentionPolicy.SOURCE):表示该注解只能保存在java源文件中 @Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中 @Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且能被反射机制所读取
-
-
当注解中仅有一个名为
value
的属性时,指定属性值时可以忽略该属性名,比如上面的两个元注解