Java基础知识点

145 阅读15分钟
  • JDK Java Development Kit: java开发工具包, 包含JRE
  • JRE Java Runtime Environment: Java运行时环境 JVM和核心类库
  • JVM Java Virtual Machine: Java 虚拟机

JavaSe中常见的包

java.lang 基础包, 自动导入

java.io IO流的包

java.util 常用的工具类

Java中的数据类型

基本数据类型
    byte     Byte
    short    Short
    int      Integer
    long     Long
    float    Float
    double   Double
    boolean  Boolean
    char     Character
    
右侧为包装类, 包装类有一个好用的方法 
int i = 20;
String s1 = String.valueOf(i); // 会使用缓存
String s2 = i.toString();

JDK1.5之后, 支持自动装箱拆箱操作
Integer in1 = new Integer(10);
int in2 = in1; // 不必使用in1.toInt();


除了boolean和Character外, 其他包装类都有valueOf()和parsexxx方法

引用数据类型
    class
    interface
    array

关于Class

Class 是方法和属性的集合体, 面向对象的基本单元

定义在类中的变量叫成员变量, 在方法中定义的变量叫局部变量
每个类都有一个构造器, 至少有一个无参构造器来生成对象, 可以有多个, 但参数列表必须不同
当局部变量和成员变量重名的时候可以使用this关键字, 表示当前调用的对象, 如果使用this去调用构造方法, 则必须放在方法的首行, 否则会报错, static(类方法/静态方法)方法中不能使用this

static修饰符

修饰的属性表示静态变量, 可以通过类名访问
修饰的成员方法为静态方法, 通过类名访问, 静态方法不存在多态
修饰的代码块, 当JVM加载类时, 会优先执行该代码块
修饰的成员变量和方法时, 会被这些类的实例共享

单例模式

懒汉式

class Single {
    public  static  Single ss;

    private Single() {}

    public static Single instance() {
        if (ss == null) {
            ss = new Single();
        }
        return ss;
    }
}

饿汉式

class Single {
    public  static  Single ss;

    static {
        ss = new Single();
    }

    private Single() {}

    public static Single instance() {
        return ss;
    }
}

抽象类 和 接口

- 抽象类被关键字abstract修饰的类
- 抽象类不能被实例化
- 抽象类可以定义抽象方法, 但不能有实现, 抽象方法必须被子类重写
- 含有抽象方法的类必须被定义为抽象类
- 构造方法不能使用abstract, 抽象类中不一定要有抽象方法

接口可以看做是抽象类的一种特殊存在

- 接口中只能定义抽象方法和变量
    方法是抽象方法
    变量的修饰符  publick static final int = 10;
- 接口同样不能被实例化, 也没有构造函数的概念, 接口中的所有方法必须被实现

String StringBuffer StringBuilder

String类是不可变的, 声明对象之后的操作, 都会产生一个新的对象
    比较两个字符串是否相等不能使用 == 要用equals()  
        == 会比较内存地址

StringBuffer成为字符串缓冲区, 工作原理是, 预先申请一块内存, 存放字符, 如果满了会继续申请, 是可变对象, 所有方法都是同步的

StringBuffer sbStr = new StringBuffer();
sbStr.append("");

StringBuilder 速度快, 但不是线程安全

日期处理


Date today = new Date();

// 格式化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.format(today);

// 日历
Calendar c = Calendar.getInstance();
c.get(Calendar.DAY_OF_MONTY);

// 获取某天是周几
Date d = new SimpleDateFormat("yyyy-MM-dd").parse(2000-10-01");
c.setTime(d);
c.get(Calendar.DAY_OF_WEEK);

数字处理

DecimalFormat df = new DecimalFormat("###,###.##");
df.format(1233.234324);

// 不够补零
new DecimalFormat("###,###.0000").format(12345.12);

// 精确计算
BigDecimal b1 = new BigDecimal("2.0");
BigDecimal b2 = new BigDecimal("1.1");
b1.subtract(b2);

截断函数


ceil  取高值
floor  取低值
round  四舍五入
random()* 10 + 10; // 10 - 20的随机数

内部类


- 在类的内部定义一个类
- 内部类是一种编译时语法
- 内部类分为四种
    - 成员内部类
    - 静态内部类
    - 局部内部类
    - 匿名内部类
    
内部类的作用
    可以访问外部类的私有成员
    配合接口, 抽象类实现多继承, 主要作用
    接口公开, 却把接口的实现类作为内部类隐藏起来, 强制用户通过接口来访问接口的实现, 强制达到弱耦合性
    
作用一
class Outer {
    private int index;
    public  Outer(int index) {
        this.index = index;
    }

    class Inner {
        private int index = 100;
        public void  print() {
            System.out.println(this.index);
            System.out.println(Outer.this.index);
        }
    }
}


Outer out1 = new Outer(10);

Outer.Inner in = out1.new Inner();
in.print(); // 10 100 


作用二

abstract class Person {
    public abstract void  run();
}

interface Machine {
    void run();
}

class Robot extends Person {
    class RobotHeart implements Machine {
        public void run() {
            System.out.println("机器人发动机");
        }
    }

    public void run() {
        System.out.println("人在跑");
    }
}


Robot r = new Robot();
r.run();
r.new RobotHeart().run();

静态内部类


- 用static修饰的内部类
- 可以有静态成员
- 只能访问外部的静态类成员
- 构造静态内部类时, 不需要外部类对象

class Outer {
    private int index;
    private static int a = 200;

    static class Inner {
        private  static  int b = 100;
        public void print() {
//            System.out.println(Outer.this.a); // 报错 'Outer.this' cannot be referenced from a static context
            System.out.println(a + " " + b);
        }
    }
}


Outer.Inner in = new Outer.Inner();
in.print();

局部内部类

- 定义在外部类方法中的内部类称为局部内部类, 不能使用访问修饰符, 但可以使用 abstractfinal 修饰符
- 可以访问外部类的属性, 还可以访问外部类的有效的局部变量, 但要求这个变量是final, jdk7中要求的必须是final 


interface MyInner {
    void print();
}

class Outer {
    private int index = 100;
    public MyInner f1() {
        int b1 = 200;
        int b2 = 300;

        class Inner implements MyInner {
            int a = 300;

            public void print() {
                System.out.println(index);
                System.out.println(a + "----" + b1);
            }
        }
        return new Inner();
    }
}


// 实例化  相当于是隐藏了内部实现
Outer o = new Outer();
o.f1().print();

匿名内部类

- 匿名内部类是一个特殊的内部类
- 这个内部类一定是用来继承一个类或者实现一个接口的, 而且我们只会创建这个内部类的一个对象
- 可以出现在方法的返回类型中, 也可以出现在方法的参数中
- 不能定义构造方法

abstract class Car {
    public abstract void run();
}

class Person {
    public static void main(String[] args) {
        Person p = new Person();
        // 抽象类不能实例化 但这里是 new Car() {  // 实现抽像方法run() }
        p.drive(new Car(){
            public void run() {
                System.out.println("jieda");
            }
        });
    }

    public void drive(Car c) {
        c.run();
    }
}

数组

java中有两种声明数组的方式
    type[] arr_name;
    type arr_name[];
    
静态初始化
    int a[] = {1, 2, 3};

动态初始化, 需要指定大小
    int a[] = new int[3];
    a[0] = 1;

数组是引用类型, 所以一般赋值就是指针指向, 如果想实现拷贝有两种方式

方式一:  普通循环赋值, 新建一个数组对象
方式二: 使用System.arraycopy(source, start, des, des_start, length)
方式三: java.util.Arrays.copyOf()内部调用的是 上面这个

    int[] a = {1, 2, 3};
    int[] b = new int[a.length];

    System.arraycopy(a, 0, b, 0, a.length);

异常

  • Java程序如果出现异常, 会自动产生一个异常类对象, 该对象将被提交给Java运行时, 然后抛出异常
  • 当Java运行时接收到异常对象时, 会寻找能处理这个异常的对象, 这一过程叫捕获异常
  • 如果找不到, 则系统终止
try {
    // xxxx
} catch (FileNotFoundException e) {
    e.getMessage(); // 获取异常信息
} catch (EOFException e) {
    e.printStackTrace();  // 打印堆栈
} catch (IOException e) {
    
} finally {
    
}

集合

List
    ArrayList, 基于可变数组, 查询快, 添加/删除慢
    LinedList, 基于链表, 查询慢, 添加删除比较快
    Stack: 被LinkedList取代
    Vector: 方法都是同步的, 效率慢, 被ArrayList 取代
    

List list = new ArrayList();

list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.remove(0);

list.get(0);

System.out.println(list);

// 普通遍历
for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i) + " ");
}

// for In 循环
for (Object i : list) {
    System.out.println(i);
}

// 迭代器循环
Iterator it = list.iterator();

while (it.hasNext()) {
    String s = (String)it.next();
    System.out.println(s);
}

// 迭代器和for结合
for (Iterator it1 = list.iterator(); it1.hasNext();) {
    String s = (String)it1.next();
    System.out.println(s);
}

Set
    无序集合, 主要有HashSet和TreeSet
    哈希表: 基于数组的, 一旦创建不能扩展, 单向链表
        判断重复数据的依据是equals()和hashCode()方法
    
    TreeSet
        SortedSet接口实现类
        可以排序
        必须实现Comparable接口, 或者调用构造函数, 传递一个排序规则
        TreeSet过滤重复的依据是, compareTo()判断结果是否为0
        

        Set<Integer> set = new TreeSet<Integer>();
        set.add(9);
        set.add(2);
        set.add(3);
        set.add(4);

        for(Iterator<Integer> it = set.iterator(); it.hasNext();) {
            Integer i = it.next();
            System.out.println(i);
        }

Map
    HashMap 
    TreeMap

常用方法
put/get/remove/clear/isEmpty/size

Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");

for (String s : map.values()) {
    System.out.println(s);
}

for (String key : map.keySet()) {
    System.out.println(map.get(key));
}

for (Map.Entry<String, String> entry: map.entrySet()) {
    entry.getKey();
    entry.getValue();
}

文件与IO流

File 类

    windows表示路径 c:\a.txt  Java中则是 c:\\a.txt 或者c:/a.txt
    File类没有无参构造方法
        File(String path_name)
        File(String parent, String child)
        File(File parent, String child)
        
        常用方法
            mkdir/mkdirs/delete/deleteOnExit()/exists/isFile/isDiectory
            getPath/getName/getParent/getAbsoutePath/list

// 创建文件
File f0 = new File("abc.txt");
f0.createNewFile();

// 创建文件夹
File f1 = new File("abc/bac");
f1.mkdirs();

File f2 = new File("cde", "bcd/java.txt");
System.out.println(f2.getParent()); // cde/bcd
System.out.println(f2.getPath()); // cde/bcd/java.txt
System.out.println(f2.getName()); // java.txt
System.out.println(f2.getAbsolutePath()); // /Users/xx/IdeaProjects/TestJava/cde/bcd/java.txt

File f5 = new File("c:");
System.out.println(f5.isDirectory()); // false

// 获取文件列表
File f = new File("/Users/xxx/IdeaProjects/TestJava");

File[] files = f.listFiles();
for (File file : files) {
    System.out.println(file.getName());
}

IO流

为进行数据的输入(input)输出(output)操作, 在java中抽象为stream, 是从source到接收sink的有序数据

输入流: 只能读取数据 输出流: 只能写入数据

按照处理的数据类型: 字节流和字符流
    字节流: 以字节为单位8个bit, InputStream OutputSteam
    字符流: 用于处理Unicode字符数据 16个bit Writer Reader
    
按照流的功能: 节点流(低级流)和处理流(高级流)
    节点流: 向一个特定的IO设备, 读写数据的流, 成为节点流, 也背成为低级流, 
        InputStream和Reader的子类都有read()的方法
        OutputStream和Writer都有write()的方法
    处理流: 实现对一个已存在的流的链接和封装, 通过所封装的流的功能调用实现数据读写功能的流成为处理流
    
使用流的原则
    流基本上是成对出现
    不使用低级流操作数据
    初始化的时候首先初始化低级流, 然后初始化高级流
    关闭的时候要关闭高级流, 同时会自动关闭低级流
    只有处理纯文本文件时, 才会使用字符流, 除此之外, 使用字节流就可以了

// abc.txt中的内容: ABC
InputStream is = null;

try {
    is = new FileInputStream("abc.txt");
    System.out.println(is.read());  // 65
    System.out.println(is.read());  // 66
    System.out.println(is.read());  // 67
    System.out.println(is.read());  // -1
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (is != null) {
            is.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 转为字符

is = new FileInputStream("abc.txt");

int len = 0;
while ((len = is.read()) != -1) {
    char c = (char)len;
    System.out.println(c);
}

以字节个数读取数据


is = new FileInputStream("abc.txt");

// 一次读取3个字节
byte[] bytes = new byte[3];

int i1 = is.read(bytes);
System.out.println(new String(bytes)); // abc

int i2 = is.read(bytes);
System.out.println(new String(bytes)); // def

int i3 = is.read(bytes);
System.out.println(new String(bytes)); // gef  为什么? 
System.out.println(new String(bytes, 0, i3)); // g

int i4 = is.read(bytes);
System.out.println("-------------");
System.out.println(i1);
System.out.println(i2);
System.out.println(i3);
System.out.println(i4);

int read(byte[] b, int off, int len)
    读取最大len个字节, 填充到数组b中, 从b的off索引处开始填充
    如果len为0, 则不读取任何字节并返回0 
    否则尝试读取至少一个字节
    如果没有可用字节, 返回-1 

文件字节输出流(FileOutputStream)


// FileOutputSteam("file_name", 是否追加); 
// 第二个参数默认为false, 写入会覆盖原内容, 为true 则是追加
FileOutputStream out = new FileOutputStream("abc.txt");

String s = "hello world";

// 将字符转为byte
byte[] b = s.getBytes();

// 将byte读取到流
out.write(b);

// 保证写入磁盘
out.flush();

字节缓冲流(BufferedStream), 提高读写效率, 减少物理读取次数

String s = "hello world kkk";

byte[] bs = s.getBytes(); // 可以设置读取编码

// 创建节点流
FileOutputStream fout = new FileOutputStream("abc.txt");

// 封装buffer流
BufferedOutputStream bout = new BufferedOutputStream(fout);

// 写数据
bout.write(bs);

// 关闭外层数据流  bout.flush() 默认执行了
bout.close();

字符集

ASCII: 长度一个字节, 8位
GB2312/GBk: 表示汉字
UTF-8:  16个字节变长编码

字符流

字符输入流

Reader
BufferedReader
InputStreamReader
FileReader

字符输出流

Write
BufferdWrite
OutputSteamWriter
PrintWriter
FileWriter

文件字符流, 不推荐使用, 无法指定字符编码

FileReader
FileWriter

推荐使用转换流

InputSteamReader
OutputStreamWriter

简单示例

public class Second {
    public static void main(String args[]) throws IOException {

        InputStream is = null;
        InputStreamReader isr = null;

        OutputStream os = null;
        OutputStreamWriter osw = null;

        try {
            is  = new FileInputStream("abc.txt");
            isr = new InputStreamReader(is, "UTF-8");

            os = new FileOutputStream("def.txt");
            osw = new OutputStreamWriter(os, "UTF-8");

            char[] value = new char[3];
            int len = 0;
            while ((len = isr.read(value)) != -1) {
                osw.write(value, 0, len);
            }

            osw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (isr != null) {
                    isr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if (osw != null) {
                    osw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

缓冲流

InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;

try {
    is = new FileInputStream("abc.txt");
    isr = new InputStreamReader(is);
    br = new BufferedReader(isr);

    String value = null;
    while ((value = br.readLine()) != null) {
        System.out.println(value);
    }

}

DataInputSteamDataOutputSteam分别继承自InputSteam和OutputSteam实现基本数据类型的读和写

ObjectInputSteamObjectOutputStream实现了对象的写入和读取, 对象需要实现接口java.io.Serializable, 这个接口没有任何方法, 只有一个标识作用

序列化示例

class Person implements Serializable {
    public String name;
}

public class Second {
    public static void main(String args[]) throws IOException {
        ObjectOutputStream oos = null;

        try {
            oos = new ObjectOutputStream(new FileOutputStream("person.dat"));
            Person p1 = new Person();
            p1.name = "kk";
            oos.writeObject(p1);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(oos != null) {
                oos.close();
            }
        }
    }
}

反序列化示例

class Person implements Serializable {
    public String name;
}

public class Second {
    public static void main(String args[]) throws IOException {
        ObjectInputStream ois = null;

        try {
            ois = new ObjectInputStream(new FileInputStream("person.dat"));

            Person p1 = (Person) ois.readObject();
            System.out.println(p1.name);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois != null) {
                ois.close();
            }
        }
    }
}

Properties类, 是HashTable的子类, 用于读取配置文件, 以.properties结尾的文件和xml文件

Properties pro = new Properties();
InputStream is = new FileInputStream("my.properties");
pro.load(is);

String v1 = pro.getProperty("key1");
String v2 = pro.getProperty("key2");

线程

线程开发有两种方式继承Thread实现Runnable接口

继承Thread

class MyThread extends Thread {
    public void run() {
        for(int i = 0; i < 100000; i++) {
            System.out.println("my Thread");
        }
    }
}

public class Second {
    public static void main(String args[]) throws IOException {
        Thread t1 = new MyThread();
        // 启动线程
        t1.start();
    }
}

实现Runnable接口

class MyRunnable implements Runnable {
    public void run() {
        for(int i = 0; i < 100000; i++) {
            System.out.println("MyRunnable");
        }
    }
}

public class Second {
    public static void main(String args[]) throws IOException {
        Runnable target = new MyRunnable();
        // runnable 依赖thread
        Thread thread = new Thread(target);
        
        thread.start();
    }
}

线程的状态

  • 起始: 采用new创建完成
  • 可运行: 执行了start
  • 运行: 开始占用CPU时间
  • 阻塞: 执行了是slee或等待
  • 终止: 退出run方法

线程优先级

  • MAX_PRIORITY 最高
  • MIN_PRIORITY 最低
  • NOM_PRIORITY 默认

Thread常用方法

  • Thread.sleep(100); 线程沉睡100毫秒, 将CPU的时间交给其他线程使用
  • t.join() 调用后, 当前线程会被阻塞不再执行, 直到被调用线程执行完毕, 当前线程才会执行
  • Thread.yield(); 只是让当前线程回到可执行状态, 让同优先级的线程有执行的机会
  • t.interrupt(); 中断线程
  • t.setFlag(true); 可以根据flag判断是否要终止线程
class MyRunnable implements Runnable {
    private boolean flag;

    public void run() {
        for(int i = 0; i < 100000; i++) {
            System.out.println("MyRunnable " + " " + i);
            if (flag) {
                break;
            }
        }
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

public class Second {
    public static void main(String args[]) throws IOException {
        MyRunnable target = new MyRunnable();
        // runnable 依赖thread
        Thread thread = new Thread(target);

        thread.start();
        try {
            // 沉睡2秒
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 设置为true
        target.setFlag(true);
    }
}

线程锁

多线程访问的问题

class  Processor implements Runnable {
    // 成员属性, 出现问题, 因为是共享的, 相当于两个线程操作一个属性
    private int s = 0;
    @Override
    public void run() {
        // 放到这里的时候不会有问题, 局部变量, 每一次运行都是hi单独的
        // private int s = 0;
        for (int i = 0; i < 10; i++) {
            s += i;
        }

        System.out.println(Thread.currentThread().getName() + ", s = " + s);
    }
}

public class Second {
    public static void main(String args[]) throws IOException {
        Processor p1 = new Processor();
        Thread t1 = new Thread(p1, "t1");
        t1.start();

        Thread t2 = new Thread(p1, "t2");
        t2.start();

    }
}

使用synchronized锁来解决问题

class  Processor implements Runnable {
    private int s = 0;
    @Override
    public void run() {
        
        // 代码越多, 效率越低
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                s += i;
            }

            System.out.println(Thread.currentThread().getName() + ", s = " + s);
            s = 0;
        }
    }
}

下面三个方法是协调线程对共享数据的存取, 所以必须在synchronized语句内使用

wait(): 使当前线程暂停执行并释放对象锁, 让其他线程进入代码块, 当前对象被放入对象等待池中

notify(): 调用此方法时, 将对象从等待池中移走一个任意的线程并放到锁标志等待池中

notifyAll(): 从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中

守护器线程: 等待所有线程结束之后才会结束, 垃圾回收器就是一个守护线程

定时器Timer

构造方法

- Timer()
- Timer(boolean isDaemon) 指定相关的线程作为守护程序运行
- Timer(String name) 指定线程名称
- Timer(String name, boolean isDaemon)

生产者消费者模式

// 馒头
class ManTou {
    int id;

    ManTou(int id) {
        this.id = id;
    }

    public String toString() {
        return "ManTou: " + id;
    }
}

/**
 * 篮子对象是有容量的,  装馒头和取馒头两个操作
 * 生产者和消费者要保证一下几点
 * 1. 同一时间内只能有一个生产者, 生产方法加锁
 * 2. 同一时间内只能有一个消费者, 消费方法加锁
 * 3. 生产者生产的同时消费者不能消费, 生产方法加锁
 * 4. 消费者消费时生产者不能生产, 消费方法加锁
 * 5. 共享空间时不能继续消费
 * 6. 共享空间满时生产者不能生产
 */
class SyncStack {

    int index = 0;
    // 容量有限, 只能装6个
    ManTou[] arrMt = new ManTou[6];

    /**
     * 该方法为同步方法
     *
     * 首先判断是否已经满了, 满的话等待, 释放同步锁 允许消费
     * 当不满时, 首先唤醒正在等待的消费方法, 只能让它进入就绪状态
     * 等生产结束释放同步方法锁后消费才能持有该锁进行消费
     *@param mt 馒头对象
     */
    public synchronized  void push(ManTou mt) {
        while (index == arrMt.length) { // 满了
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        this.notify(); // 如果取馒头的wait了, 则通知他醒来
        arrMt[index] = mt;
        index++;
    }

    public synchronized ManTou pop() {
        while (index == 0) { // 篮子空了
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        this.notify(); // 如果产馒头的wait了, 则通知他醒过来
        index--;
        return arrMt[index];
    }

}

// 生产者
class Producer implements Runnable {
    SyncStack ss = null;
    Producer(SyncStack ss) {
        this.ss = ss;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            ManTou mt = new ManTou(i);
            ss.push(mt);
            System.out.println("生产了: " + mt);
            try {
                Thread.sleep((long)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 消费者
class Consumer implements Runnable {
    SyncStack ss = null;

    Consumer(SyncStack ss) {
        this.ss = ss;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            ManTou mt = ss.pop();
            System.out.println("消费了: " + mt);
        }
    }
}


public class Second {
    public static void main(String args[]) throws IOException {
        
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);

        new Thread(c).start();
        new Thread(p).start();

    }
}

反射主要是指程序可以访问, 检测和修改它本身状态或行为的一种能力, 并根据自身状态和结果, 调整或修改应用所描述行为的状态和相关的语义

构造Class类对象的三种方式

- Class c = Class.forName("Person");
- Class c = String.class;
- Class c = s.getClass();
- java.lang.reflect

反射相关的主要类

- Class
    Class pCls = Class.forName("包名路径.Person");
    
    Person p1 = (Person)pCls.newInstance();
    // 通过构造方法创建对象
    Person p2 = (Person)c1.newInstance("kk");
- Method
    // 获取所有的方法对象, 获取不到父类的
    Method[] ms = pCls.getDeclaredMethods();
    
    // 所有方法, 包括父类
    ms = pCls.getMethods();
- Field
    Field[] fs = pCls.getFields();
    fs = pCls.getDeclaredFields();
    
- Constructor
    // 获取公共构造方法
    Constructor[] cs = pCls.getConstructors();
    
    // 获取所有构造方法
    Constructor[] cs = pCls.getDeclaredConstructors();
- Modifier

    for (Constructor c : cs) {
        // 构造方法修饰符
        Modifier.toString(c.getModifiers()));
        // 构造方法名字
        c.getName();
        
        // 参数集合
        Class[] paramCls = c.getParameterTypes();
        for (int i = 0; i < paramCls.length; i++) {
            Class clapp = paramCls[i];
            // 参数类型
            clapp.getName();
        }
    }
    
    // 指定参数的公开构造方法
    Consttructor c1 = pCls.getConstructor(String.class);
    
    Constructor c2 = pCls.getDeclaredConstructor(String.class, int.class, String.class);