当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象
在class文件进入方法和数据共享区时,在执行Main方法和静态成员变量/方法之前,会为class文件在堆里面创建一个“文件对象”,又叫字节码对象(不是new Person())!
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
这个时候才轮到静态区,静态方法静态成员赋初值!
初始化
就是我们以前讲过的初始化步骤
类初始化时机
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类
6. 直接使用java.exe命令来运行某个主类
类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
类加载器的组成
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
返回多个构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量
返回多个成员变量
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
返回获取一个方法:
public Method getMethod(String name, Class<?>... parameterTypes)
获取public 修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
返回获取多个方法:
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
示例代码:
package com.oracle.filetcp; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { Socket soc=new Socket("127.0.0.1",7001); //写到服务器用的流: OutputStream out=soc.getOutputStream(); //读取本地文件: FileInputStream fis=new FileInputStream("F:\IOTest\TestPic.jpg"); int len=0; byte[] bytes=new byte[1024]; //如果从文件里面读,是有文件末尾的,如果从流里面去读,是没有文件末尾的,没有-1,所以read方法会发生阻塞! while((len=fis.read(bytes))!=-1){ out.write(bytes,0,len); } //告诉服务器到这里已经结束了,已经到末尾了! soc.shutdownOutput(); //接收服务器端的回复: InputStream in=soc.getInputStream(); len=in.read(bytes); System.out.println(new String(bytes,0,len)); //释放资源: fis.close(); soc.close(); } } package com.oracle.filetcp; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { //明确端口号: ServerSocket server=new ServerSocket(9999); //该方法接收客户端返回过阿里的Socket对象: Socket soc=server.accept(); //服务器客户端之间的输入流,明确数据源: InputStream in=soc.getInputStream(); //明确目的地: File file=new File("F:\IOTest\Test"); if(!file.exists()){ file.mkdirs(); } //写文件: FileOutputStream fos=new FileOutputStream(file+"\TestPic0601.jpg"); //开始复制: int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len); } //回复客户端: OutputStream out=soc.getOutputStream(); out.write("上传成功".getBytes()); //释放资源: server.close(); fos.close(); } } package com.oracle.filetcp; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UPLoad implements Runnable { private Socket soc; public UPLoad(Socket soc){ this.soc=soc; } public void run(){ FileOutputStream fos=null; try{ //服务器客户端之间的输入流,明确数据源: InputStream in=soc.getInputStream(); //明确目的地: File file=new File("F:\IOTest\Test"); if(!file.exists()){ file.mkdirs(); } //写文件: fos=new FileOutputStream(file+"\TestPic0601.jpg"); //开始复制: int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len); } //回复客户端: OutputStream out=soc.getOutputStream(); out.write("上传成功".getBytes()); }catch(IOException ex){ ex.printStackTrace(); }finally{ //释放资源: try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } package com.oracle.filetcp; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class TestUPLoad { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(7001); while(true){ Socket soc=server.accept(); new Thread(new UPLoad(soc)).start(); } } } package com.oracle.filetcp; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { Socket soc=new Socket("127.0.0.1",7001); //写到服务器用的流: OutputStream out=soc.getOutputStream(); //读取本地文件: FileInputStream fis=new FileInputStream("F:\IOTest\TestPic.jpg"); int len=0; byte[] bytes=new byte[1024]; //如果从文件里面读,是有文件末尾的,如果从流里面去读,是没有文件末尾的,没有-1,所以read方法会发生阻塞! while((len=fis.read(bytes))!=-1){ out.write(bytes,0,len); } //告诉服务器到这里已经结束了,已经到末尾了! soc.shutdownOutput(); //接收服务器端的回复: InputStream in=soc.getInputStream(); len=in.read(bytes); System.out.println(new String(bytes,0,len)); //释放资源: fis.close(); soc.close(); } } package com.oracle.filetcp; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { //明确端口号: ServerSocket server=new ServerSocket(9999); //该方法接收客户端返回过阿里的Socket对象: Socket soc=server.accept(); //服务器客户端之间的输入流,明确数据源: InputStream in=soc.getInputStream(); //明确目的地: File file=new File("F:\IOTest\Test"); if(!file.exists()){ file.mkdirs(); } //写文件: FileOutputStream fos=new FileOutputStream(file+"\TestPic0601.jpg"); //开始复制: int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len); } //回复客户端: OutputStream out=soc.getOutputStream(); out.write("上传成功".getBytes()); //释放资源: server.close(); fos.close(); } }
以下代码是利用反射读取配置文件,实现不修改代码只修改Properties文件实现修改功能:
package com.oracle.fanshelast; public class Person { public void eat(){ System.out.println("人吃饭"); } } package com.oracle.fanshelast; public class Student { public void study(){ System.out.println("学生学习"); } } package com.oracle.fanshelast; public class Worker { public void work(){ System.out.println("工人工作"); } } //Properties配置文件: #className=com.oracle.fanshelast.Person #methodName=eat className=com.oracle.fanshelast.Worker methodName=work //Main方法主代码: package com.oracle.fanshelast; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties; public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //new Person().eat(); //反射配置文件实现修改代码: //把我们要运行的类和方法以键值对的形式写在文本中,我们想要运行哪个类,里面的方法只需要改配置文件即可! //步骤: /*准备配置文件 IO读取配置文件Reader 将文件中的键值对保存在我们所学的Properties集合中键值对就是类和方法名 通过反射获取指定类的Class文件对象 通过Class文件对象获取指定方法 运行方法*/ FileReader fr=new FileReader("src/Config.properties"); Properties pro=new Properties(); pro.load(fr); String className=pro.getProperty("className"); String methodName=pro.getProperty("methodName"); //这里传的是上方String className的值,即Properties文件中的键值对的值! Class cal=Class.forName(className); Object obj=cal.newInstance(); Method met=cal.getMethod(methodName); met.invoke(obj); } }