195、httpserver_准备_Socket入门
public class Server { private ServerSocket server; public static void main(String[] args) { Server server = new Server(); server.start(); } public void start() { try { server = new ServerSocket(8080); String msg = null; this.receive(); } catch (IOException e) { e.printStackTrace(); } } private void receive() { try { Socket client = server.accept(); String msg = null; BufferedInputStream bis = new BufferedInputStream(client.getInputStream()); byte[] bytes = new byte[10240]; int len = 0; while (-1 != (len = bis.read(bytes, 0, bytes.length))) { msg = new String(bytes, 0, len).trim(); } System.out.println(msg); } catch (IOException e) { e.printStackTrace(); } } }
196、httpserver_准备_html
html超文本标记语言,是用于描述网页文档的一种标记语言
<html> <head><title>第一个表单</title></head> <body> <pre> method:请求方式get/post get:数据量小,安全性不高(默认) post:数据量大,安全性相对高 action:请求的服务器路径 id:编号,前端区分唯一性,一般在js中使用 name:名称,后端(服务器)区分唯一性,获取值 只要提交数据到后台,必须存在name </pre> <form method=”get” action=”http://localhost:8080/index.html”> 用户名: <input type=”text” name=”uname” id=”uname” /> 密码: <input type=”password” name=”pwd” id=”pwd” /> <input type=”submit” value=”登录” /> </form> </body> </html>
197、httpserver_准备_http协议
应用层HTTP、FTP、TELNET、SNMP、DNS
传输层TCP、UDP
网络层IP
HTTP(超文本传输协议)是网络应用层的协议,建立在TCP/IP协议基础上,http协议使用可靠的TCP连接,默认端口为80,目前最新版本为HTTP1.1,对应的RFC文档为RFC2068
HTTP协议简介:用户打开web浏览器,输入url地址,就能接收到远程HTTP服务器端发送过来的网页,即HTTP遵循请求(request)/响应(response)模型,web浏览器向web服务器发送请求,web服务器处理请求并返回对应的相应。所有HTTP连接都构造成一套请求和响应
HTTP客户端和服务器分别由不同的软件开发商提供,它们都可以用任意编程语言编写。如用.net编写的客户端程序与用java编写的服务器顺利通信,就必须遵守HTTP协议,这样才能彼此都懂对方发送的消息,HTTP协议严格规定了HTTP请求和HTTP相应的数据格式
HTTP协议规定,HTTP请求由3部分构成:
请求方法、URI(统一资源定位符)、HTTP协议/版本:
请求的第一行是“方法 URL协议/版本”,根据HTTP标准,HTTP请求可以使用多种请求方法,例如HTTP1.1支持7种请求方法(GET、POST、HEAD、OPTIONS、PUT、DELETE、TARCE),在internet应用中,最常用的方法是GET和POST。URL完整地指定了要访问的网络资源,相对服务器的根目录的相对目录即可,因此总是以“/”开头。最后,协议版本声明了通信过程中使用HTTP的版本
POST /index.html HTTP/1.1
请求头(RequestHeader):
请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器所用语言,请求正文的长度等
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
请求正文(Request Content)(post方式才存在):
请求头和请求正文之间必须存在CRLF(\r\n)符号(回车符和行结束符),与请求头分开。这个行非常重要,它表示请求头已经结束,接下来的是请求正文。通常POST方式的数据存放于此请求正文中可以包含客户提交的查询字符串信息
uname=abc&pwd=123
HTTP响应
HTTP/1.1 200 ok
HTTP响应码也称为状态码,它反映了web服务器处理HTTP请求状态。HTTP响应码由3位数字构成,其中首位数字定义了响应码的类型
1XX信息类,表示收到web浏览器请求,正在进一步处理中
2XX成功类,表示用户请求被正确接收,理解和处理,例如200 OK
3XX重定向类,表示请求没有成功,客户必须采取进一步动作
4XX客户端错误,表示客户端提交的请求有错误,例如404 NOT FOUND表示请求中所引用的文档不存在
5XX服务器错误,表示服务器不能完成对请求的处理
HTTP响应格式
HTTP协议规定,响应和请求一样,有三部分构成
HTTP协议版本 状态码 描述
响应头(Response Head)
响应正文(Response Content)
198、httpserver_准备_http工具
199、httpserver_封装Response
200、httpserver_封装Request_method_url
201、httpserver_封装Request_存储参数_处理中文
202、httpserver_封装分发器
203、httpserver_多请求处理_多态
204、httpserver_多请求处理_反射
205、httpserver_xml配置文件_sax解析基础
206、httpserver_xml配置文件_sax解析应用
207、httpserver_整合最终版
动态语言在运行期随意改变对象的类型和结构,java不是动态语言,但是具有动态属性(反射)。
反射:通过对象找出类名;在运行期动态创建对象分析对象(属性、方法);jvm在创建时自动生成与之对应的Class对象,同一个类的多个对象在jvm中只有一个对应的Class对象
获取Class对象的方式:对象.getClass()、例如Student.class、Class.forName(“完整类名”)
public class Test { public static void main(String[] args) throws ClassNotFoundException { String str = "abc"; Class<?> cls = str.getClass(); cls = String.class; cls = Class.forName("java.lang.String"); } }
XML可扩展标记语言,基本解析方式有两种:SAX基于事件流的解析和DOM基于XML文档树结构的解析。常见的四种解析方式有:DOM、SAX、DOM4J、JDOM
<?xml version="1.0" encoding="UTF-8"?> <data> <person> <name>张三</name> <age>66</age> </person> <person> <name>李四</name> <age>65</age> </person> </data>
public class Test { public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { SAXParserFactory factory = SAXParserFactory.newInstance();// 获取解析工厂 SAXParser parser = factory.newSAXParser();// 获取解析器 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("cn/nwn/xml/person.xml"); PersonHandler handler = new PersonHandler(); parser.parse(is, handler);// 加载document注册处理器 List<Person> persons = handler.getPersons(); System.out.println(persons); } } class PersonHandler extends DefaultHandler { private List<Person> persons; private Person person; private String tag; public List<Person> getPersons() { return persons; } @Override public void startDocument() throws SAXException { persons = new ArrayList<Person>(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (null != qName) { tag = qName; } if (null != qName && qName.equals("person")) { person = new Person(); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { String str = new String(ch, start, length); if (null != tag && tag.equals("name")) { person.setName(str); } else if (null != tag && tag.equals("age")) { Integer age = Integer.valueOf(str); person.setAge(age); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equals("person")) { persons.add(person); } tag = null; } @Override public void endDocument() throws SAXException { super.endDocument(); } } class Person { private String name; private int age; public Person() { } public Person(String name, int age) { super(); this.name = name; this.age = age; } 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; } @Override public String toString() { return "(name=" + name + ", age=" + age + ")"; } }
httpserver整合最终版
public class Server { private ServerSocket server; private static final String CRLF = "\r\n"; private static final String BLANK = " "; private boolean isShutDown = false; public static void main(String[] args) { Server server = new Server(); server.start(8888); } public void start(int port) { try { server = new ServerSocket(port); String msg = null; this.receive(); } catch (IOException e) { stop(); } } private void receive() { try { while (!isShutDown) { new Thread(new Dispatcher(server.accept())).start(); } } catch (IOException e) { stop(); } } private void stop() { isShutDown = true; CloseUtil.closeAll(server); } }
public class Dispatcher implements Runnable { private Socket client; private Request req; private Response res; private int code = 200; public Dispatcher(Socket client) { this.client = client; try { req = new Request(client.getInputStream()); res = new Response(client.getOutputStream()); } catch (IOException e) { code = 500; return; } } @Override public void run() { try { Servlet serv = WebApp.getServlet(req.getUrl()); if (null == serv) { code = 404; } else { serv.service(req, res); } res.pushToClient(code); } catch (Exception e) { try { res.pushToClient(500); } catch (IOException e1) { e1.printStackTrace(); } } CloseUtil.closeAll(client); } }
public class Request { private static final String CRLF = "\r\n"; private String method; private String url; private Map<String, List<String>> parameterMapValues; private String requestInfo; private InputStream is; public Request() { method = ""; url = ""; parameterMapValues = new HashMap<String, List<String>>(); requestInfo = ""; } public Request(InputStream is) { this(); this.is = is; try { byte[] data = new byte[20480]; int len = is.read(data, 0, data.length); requestInfo = new String(data, 0, len); } catch (IOException e) { return; } parseRequestInfo(); } public String getMethod() { return method; } public String getUrl() { return url; } public String getParameterMapValues() { return parameterMapValues.toString(); } private void parseRequestInfo() {// 分析请求信息 if (null == requestInfo || (requestInfo = requestInfo.trim()).equals("")) { return; } String paramString = ""; String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF)); this.method = firstLine.substring(0, firstLine.indexOf("/")).trim(); String urlStr = firstLine.substring(firstLine.indexOf("/"), firstLine.indexOf("HTTP/")).trim(); this.url = urlStr; if (this.method.equalsIgnoreCase("POST")) { String lastLine = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim(); paramString = lastLine; } else if (this.method.equalsIgnoreCase("GET")) { if (urlStr.contains("?")) { String[] urlArray = urlStr.split("\\?"); this.url = urlArray[0]; paramString = urlArray[1]; } } if (paramString.equals("")) { return; } parseParams(paramString); } private void parseParams(String paramString) { StringTokenizer token = new StringTokenizer(paramString, "&"); while (token.hasMoreTokens()) { String keyValue = token.nextToken(); String[] keyValues = keyValue.split("="); if (keyValues.length == 1) { keyValues = Arrays.copyOf(keyValues, 2); keyValues[1] = null; } String key = keyValues[0].trim(); String value = null == keyValues[1] ? null : decode(keyValues[1].trim(), "GBK"); if (!parameterMapValues.containsKey(key)) { parameterMapValues.put(key, new ArrayList<String>()); } List<String> values = parameterMapValues.get(key); values.add(value); } } private String decode(String value, String code) { try { return URLDecoder.decode(value, code); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public String getParameter(String name) { String[] values = getParameterValues(name); if (null == values) { return null; } return values[0]; } public String[] getParameterValues(String name) { List<String> values = null; if ((values = parameterMapValues.get(name)) == null) { return null; } return values.toArray(new String[0]); } }
public class Response { private static final String CRLF = "\r\n"; private static final String BLANK = " "; private StringBuilder headInfo;// 头信息 private StringBuilder content; private int len;// 正文长度 private BufferedWriter bw; public Response() { headInfo = new StringBuilder(); content = new StringBuilder(); len = 0; } public Response(OutputStream os) { this(); bw = new BufferedWriter(new OutputStreamWriter(os)); } public Response println(String info) { content.append(info).append(CRLF); len += (info + CRLF).getBytes().length; return this; } private void createHeadInfo(int code) { headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK); switch (code) { case 200: headInfo.append("OK"); break; case 404: headInfo.append("NOT FOUND"); break; case 505: headInfo.append("SERVER ERROR"); break; } headInfo.append(CRLF); headInfo.append("Server:test Server/0.0.1").append(CRLF); headInfo.append("Date:").append(new Date()).append(CRLF); headInfo.append("Content-type:text/html;charset=GBK").append(CRLF); headInfo.append("Content-Length:").append(len).append(CRLF); headInfo.append(CRLF); } public void pushToClient(int code) throws IOException { createHeadInfo(code);// 构建头信息 bw.append(headInfo.toString()); bw.append(content.toString()); bw.flush(); CloseUtil.closeAll(bw); } }
public class WebApp { private static ServletContext context; static { context = new ServletContext(); Map<String, String> servlet = context.getServlet(); Map<String, String> mapping = context.getMapping(); SAXParserFactory factory = SAXParserFactory.newInstance();// 获取解析工厂 SAXParser parser; try { parser = factory.newSAXParser(); InputStream is = Thread.currentThread().getContextClassLoader() .getResourceAsStream("WEB_INFO/web.xml"); WebHandler handler = new WebHandler(); parser.parse(is, handler);// handler处理xml生成两个List分别对应servlet和mapping List<Entity> entityList = handler.getEntityList(); for (Entity e : entityList) { servlet.put(e.getName(), e.getCls());// put进容器 } List<Mapping> mappingList = handler.getMappingList(); for (Mapping m : mappingList) { for (String url : m.getUrlPattern()) { mapping.put(url, m.getName());// put进容器(多对一关系) } } } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); } } public static Servlet getServlet(String url) throws InstantiationException, IllegalAccessException, ClassNotFoundException { if ((null == url) || (url = url.trim()).equals("")) { return null; } String qualifiedName = context.getServlet().get(context.getMapping().get(url)); return (Servlet) Class.forName(qualifiedName).newInstance();// 通过反射创建Servlet对象 } }
public class ServletContext { private Map<String, String> mapping; private Map<String, String> servlet; public ServletContext() { mapping = new HashMap<String, String>();// url->servlet别名->servlet servlet = new HashMap<String, String>(); } public Map<String, String> getMapping() { return mapping; } public void setMapping(Map<String, String> mapping) { this.mapping = mapping; } public Map<String, String> getServlet() { return servlet; } public void setServlet(Map<String, String> servlet) { this.servlet = servlet; } }
public class WebHandler extends DefaultHandler { private List<Entity> entityList; private Entity entity; private List<Mapping> mappingList; private Mapping mapping; private String beginTag; private boolean isMapping; public List<Entity> getEntityList() { return entityList; } public List<Mapping> getMappingList() { return mappingList; } @Override public void startDocument() throws SAXException { entityList = new ArrayList<Entity>(); mappingList = new ArrayList<Mapping>(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (null != qName) { beginTag = qName; if (qName.equals("servlet")) { isMapping = false; entity = new Entity(); } else if (qName.equals("servlet-mapping")) { isMapping = true; mapping = new Mapping(); } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (null != beginTag) { String str = new String(ch, start, length); if (isMapping) { if (beginTag.equals("servlet-name")) { mapping.setName(str); } else if (beginTag.equals("url-pattern")) { mapping.getUrlPattern().add(str); } } else { if (beginTag.equals("servlet-name")) { entity.setName(str); } else if (beginTag.equals("servlet-class")) { entity.setCls(str); } } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (null != qName) { if (qName.equals("servlet")) { entityList.add(entity); } else if (qName.equals("servlet-mapping")) { mappingList.add(mapping); } } beginTag = null; } @Override public void endDocument() throws SAXException { } }
public class Entity { private String name; private String cls; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCls() { return cls; } public void setCls(String cls) { this.cls = cls; } @Override public String toString() { return "(name=" + name + ", cls=" + cls + ")"; } }
public class Mapping { private String name; private List<String> urlPattern; public Mapping() { urlPattern = new ArrayList<String>(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<String> getUrlPattern() { return urlPattern; } public void setUrlPattern(List<String> urlPattern) { this.urlPattern = urlPattern; } @Override public String toString() { return "(name=" + name + ", urlPattern=" + urlPattern + ")"; } }
public abstract class Servlet { protected void service(Request req, Response res) throws Exception { this.doGet(req, res); this.doPost(req, res); } public abstract void doGet(Request req, Response res) throws Exception; public abstract void doPost(Request req, Response res) throws Exception; }
public class CloseUtil { public static void closeAll(Closeable... io) { for (Closeable tmp : io) { if (null != tmp) { try { tmp.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
public class LoginServlet extends Servlet { public boolean login(String uname, String pwd) { return uname.equals("abc") && pwd.equals("123"); } @Override public void doGet(Request req, Response res) throws Exception { String uname = req.getParameter("uname"); String pwd = req.getParameter("pwd"); if (login(uname, pwd)) { res.println("登录成功"); } else { res.println("登录失败"); } } @Override public void doPost(Request req, Response res) throws Exception { } }
public class RegisterServlet extends Servlet { @Override public void doGet(Request req, Response res) throws Exception { } @Override public void doPost(Request req, Response res) throws Exception { res.println("注册成功"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>login</servlet-name> <servlet-class>cn.nwn.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> <url-pattern>/log</url-pattern> </servlet-mapping> <servlet> <servlet-name>reg</servlet-name> <servlet-class>cn.nwn.servlet.RegisterServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>reg</servlet-name> <url-pattern>/reg</url-pattern> </servlet-mapping> </web-app>
208、注解_Annotation_内置注解
Annotation是从jdk5.0开始引入的新技术。Annotation并不是程序本身,它可以对程序做出解释。可以被其他程序比如编译器等读取。
Annotation是以“@注释名”在代码中存在,还可以添加一些参数值,例如:@SuppressWarnings(value=”unchecked”)。
Annotation可以附加在package、class、method、field等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
内置注解
@Override定义在java.lang.Override中,此注释只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
@Deprecated定义在java.lang.Deprecated中,此注释可以修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择
@SuppressWarnings定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
@SuppressWarnings的可选参数有:deprecation(使用了过时的类或方法警告)、unchecked(执行了未检查的转换时的警告,如使用集合时为制定泛型)、fallthrough(当在switch语句使用时发生case穿透)、path(在类路径、源文件路径等中有不存在路径的警告)、serial(当在可序列化的类上缺少serialVersionUID时的警告)、finally(任何finally子句不能完成时的警告)、all(关于以上所有情况的警告)。@SuppressWarnings(”unchecked”)或@SuppressWarnings(value={”unchecked”,”deprecation”})
209、自定义注解
使用@interface字段定义注解时,自动集成了java.lang.annotation.Annotation接口,其格式为public @interface 注解名{定义体}
元注解@Target:用于描述注解的使用范围。@Target(value=ElementType.Type),ElementType的取值有PACKAGE(包)、TYPE(类、接口、枚举、Annotation类型)、CONSTRUCTOR(构造器)、FIELD(域)、METHOD(方法)、LOCAL_VARIABLE(局部变量)、PARAMETER(参数)
元注解@Retention:表示需要在什么级别保存该注释信息,用于描述注释的生命周期。@Retention(RetentionPolicy.RUNTIME),RetentionPolicy的取值有SOURCE(在源文件中有效)、CLASS(在class文件中有效)、RUNTIME(在运行时有效,可以被反射机制读取)
210、反射机制读取注解
@Target(value = { ElementType.TYPE }) @Retention(value = RetentionPolicy.RUNTIME) public @interface MyTable { String value(); }
@Target(value = { ElementType.FIELD }) @Retention(value = RetentionPolicy.RUNTIME) public @interface MyField { String colName(); String colType(); int colLength(); }
public class Test { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.nwn.test.User"); Annotation[] annotations = cls.getAnnotations();// 获取类的所有有效注解(类上的一个或多个) for (Annotation a : annotations) { System.out.println(a); } // @cn.nwn.test.MyTable(value=tbl_User) Annotation annotation = cls.getAnnotation(MyTable.class);// 获取特定的注解 System.out.println(annotation);// @cn.nwn.test.MyTable(value=tbl_User) Field field = cls.getDeclaredField("name");// 获取特定属性 MyField myField = field.getAnnotation(MyField.class);// 获取特定属性的注解 System.out.println(myField.colName() + "\t" + myField.colType() + "\t" + myField.colLength()); } } @MyTable(value = "tbl_User") class User { @MyField(colName = "name", colType = "varchar", colLength = 50) private String name; @MyField(colName = "age", colType = "int", colLength = 3) private int age; public User() { } public User(String name, int age) { super(); this.name = name; this.age = age; } 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; } }
211、反射机制_介绍
动态语言是在程序运行时,可以改变程序的结构或变量的类型,例如:Phython、ruby、javascript等(var s=”var a=3;var b=5;alert(a+b)”;eval(s);)。java并不是动态语言,但是有一定的动态性,我们可以利用反射机制,字节码操作来获得类似动态语言的特性
反射机制指的是可以于运行时加载、探知、使用编译期间完全未知的类。程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。加载完类后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类结构信息,我们可以通过这个对象看到类的结构
Class cls=Class.forName(”cn.nwn.test.User”);
java.lang.Class类十分特殊,它用来表示java中所有类型的本身,Class类的对象包含了某个被加载类的结构,一个被加载的类对应一个Class对象。当一个class被加载或当加载器(class loader)的defineClass()被jvm调用,jvm便自动产生一个Class对象。Class类是reflection的根源,针对任何你想动态加载、运行的类,都要先获得相应的Class对象
public class Test { public static void main(String[] args) throws ClassNotFoundException { Class<?> cls1 = Class.forName("cn.nwn.test.User");// 对象是表示或封装一些数据,jvm会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中,这个Class对象就像一面镜子一样,通过这面镜子可以看到对应类的全部信息 Class<?> cls2 = Class.forName("cn.nwn.test.User"); System.out.println(cls1 == cls2);// true(一个类只有一个Class对象) Class<?> strCls1 = String.class; Class<?> strCls2 = "str".getClass(); System.out.println(strCls1 == strCls2); Class<?> intCls = int.class; System.out.println(intCls);// int Class<?> arrCls1 = new int[10].getClass(); Class<?> arrCls2 = new int[20].getClass(); Class<?> arrCls3 = new int[10][5].getClass(); System.out.println(arrCls1 == arrCls2);// true与长度无关 System.out.println(arrCls1 == arrCls3);// false与维度有关 } } class User { private String name; public User() { } public User(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
212、反射机制_应用反射API获取类信息
public class Test { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.nwn.test.User"); String name = cls.getName();// cn.nwn.test.User(包名加类名) String simpleName = cls.getSimpleName();// User(类名) Field[] fields1 = cls.getFields();// 只能获得public的属性 Field[] fields2 = cls.getDeclaredFields();// 获得所有属性 Field field = cls.getDeclaredField("name");// private java.lang.String cn.nwn.test.User.name Method[] methods1 = cls.getMethods();// public Method[] methods2 = cls.getDeclaredMethods(); Method method1 = cls.getMethod("getName", null); Method method2 = cls.getMethod("setName", String.class);// 第二个参数区分method的参数 Constructor[] constructors1 = cls.getConstructors();// public Constructor[] constructors2 = cls.getDeclaredConstructors(); Constructor constructor1 = cls.getConstructor(); Constructor constructor2 = cls.getConstructor(String.class, int.class); } } class User { private String name; private int age; public User() { } public User(String name, int age) { super(); this.name = name; this.age = age; } 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; } }
public class Test { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.nwn.test.User"); User user1 = (User) cls.newInstance();// 调用了User的无参构造器 Constructor<?> c = cls.getDeclaredConstructor(String.class, int.class); User user2 = (User) c.newInstance("张三", 66); User user3 = (User) cls.newInstance(); Method setNameMethod = cls.getDeclaredMethod("setName", String.class); setNameMethod.invoke(user3, "李四"); Method setAgeMethod = cls.getDeclaredMethod("setAge", int.class); setAgeMethod.invoke(user3, 65); User user4 = (User) cls.newInstance(); Field nameField = cls.getDeclaredField("name"); nameField.setAccessible(true);// 跳过安全检查 nameField.set(user4, "王五"); Field ageField = cls.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(user4, 65); System.out.println(nameField.get(user4));// System.out.println(user4.getName()); System.out.println(ageField.get(user4));// System.out.println(user4.getAge()); } } class User { private String name; private int age; public User() {// javabean必须要有无参构造 } public User(String name, int age) { super(); this.name = name; this.age = age; } 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; } }
213、反射机制_提高反射效率
setAccessible启用或禁用访问安全检查的开关,值为true则指示反射的对象在使用时应该取消java语言访问检查,值为false则指示反射的对象应该实施java语言访问检查。禁止安全检查可以提高反射的运行效率
public class Test { public static void main(String[] args) throws Exception { test1(); test2(); test3(); } public static void test1() { User user = new User(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000L; i++) { user.getName(); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); } public static void test2() throws Exception { User user = new User(); Class cls = user.getClass(); Method method = cls.getMethod("getName", null); // method.setAccessible(true); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000L; i++) { method.invoke(user, null); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); } public static void test3() throws Exception { User user = new User(); Class cls = user.getClass(); Method method = cls.getMethod("getName", null); method.setAccessible(true); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000L; i++) { method.invoke(user, null); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); } } class User { private String name; private int age; public User() { } public User(String name, int age) { super(); this.name = name; this.age = age; } 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; } }
反射操作泛型
java采用泛型擦除机制来引用泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有的和泛型有关的类型全部擦除。为了通过反射操作这些类型以迎合实际开发的需要,java新增了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又有和原始类型齐名的类型
public class Test { public static void main(String[] args) throws Exception { Method method1 = Test.class.getMethod("test01", Map.class, List.class); Type[] parameterTypes = method1.getGenericParameterTypes();// 获得方法参数的类型 for (Type parameterType : parameterTypes) { if (parameterType instanceof ParameterizedType) { Type[] genericTypes = ((ParameterizedType) parameterType).getActualTypeArguments(); for (Type genericType : genericTypes) { System.out.println(parameterType + "\t" + genericType); } } } Method method2 = Test.class.getMethod("test02", null); Type returnType = method2.getGenericReturnType(); if (returnType instanceof ParameterizedType) { Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments(); for (Type genericType : genericTypes) { System.out.println(returnType + "\t" + genericType); } } } public void test01(Map<String, User> map, List<User> list) { } public Map<Integer, User> test02() { return null; } } class User { private String name; private int age; public User() { } public User(String name, int age) { super(); this.name = name; this.age = age; } 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; } }
反射操作注解(详见210)
214、动态编译_DynamicCompil
java6.0引入动态编译机制,动态编译的应用场景可以做一个浏览器端编写java代码,上传服务器编译和运行的在线评测系统,服务器动态加载某些类文件进行编译
动态编译的两种做法
通过Runtime调用javac,启动新的进程去操作
Runtime run = Runtime.getRuntime();
Process process = run.exec(”java -cp d:/tst/HelloWorld.java”);
通过JavaCompiler动态编译
public class Test { public static void main(String[] args) throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); int result = compiler.run(null, null, null, "d:/tst/HelloWorld.java"); System.out.println(result == 0 ? "编译成功" : "编译失败"); Runtime run = Runtime.getRuntime(); Process process = run.exec("java -cp d:/tst HelloWorld");// 通过Runtime.getRuntime()动态运行编译好的类 URL[] urls = new URL[] { new URL("file:/" + "d:/tst/") };// 通过反射动态运行编译好的类 URLClassLoader loader = new URLClassLoader(urls); Class cls = loader.loadClass("HelloWorld"); Method method = cls.getMethod("main", String[].class); method.invoke(null, (Object) new String[] {});// 必须转成Object } }
215、Rhino引擎
java脚本引擎是从jdk6.0之后添加的新功能
脚本引擎使得java应用程序可以通过一套固定的接口和各种脚本引擎交互,从而达到在java平台上调用各种脚本语言的目的。java脚本API是连通java平台和脚本语言的桥梁。可以把一些复杂异变的业务逻辑交给脚本语言处理,大大提高了开发效率
通过java脚本API可以获取脚本程序输入,通过脚本引擎运行脚本并返回运行结果,这是最核心的接口。通过脚本引擎的运行上下文在脚本和java平台间交换数据。通过java应用程序调用脚本函数
Rhino是一种使用java语言编写的javascript的开源实现,现在被集成进入jkd6.0
public class Test { public static void main(String[] args) throws Exception { ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine engine = sem.getEngineByName("javascript"); String str = "var user={name:\"张三\",age:18};"; str += "print(user.name);"; engine.eval(str);// 执行javascript脚本 engine.put("msg", "test test test");// 定义变量 engine.eval("msg=\"test1 test1 test1\";"); System.out.println(engine.get("msg")); engine.eval("function sum(a,b){return a+b;}"); Invocable invo = (Invocable) engine; Object result = invo.invokeFunction("sum", new Object[] { 10, 20 });// 执行脚本函数 System.out.println(result); str = "var list=java.util.Arrays.asList([\"张三\",\"李四\"]);"; engine.eval(str); List<String> list = (List<String>) engine.get("list"); for (String tmp : list) { System.out.println(tmp); } FileReader fr = new FileReader(new File("d:/tst/test.js")); engine.eval(fr); fr.close(); } }
216、字节码操作_javassist
java动态性的两种常见实现方式:反射、字节码操作
运行时操作字节码可以实现如下功能:动态生成新的类、动态改变某个类的结构(添加/删除/修改/新的属性/新的方法)
操作字节码比反射开销小性能高
javassist性能高于反射,低于ASM
常见的字节码操作类库BCEL、ASM、CGLIB、Javassit
public class Test { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("cn.nwn.test.Student"); CtField field = CtField.make("private String name;", cc); cc.addField(field); CtMethod method = CtMethod.make("public String getName(){return name;}", cc); cc.addMethod(method); CtClass stringType = pool.get("java.lang.String");// CtClass.intType; CtConstructor constructor = new CtConstructor(new CtClass[] { stringType }, cc); constructor.setBody("{this.name=name;}"); cc.addConstructor(constructor); cc.writeFile("d:/tst"); } }
217、字节码操作_javaassist
public class Test { public static void main(String[] args) throws Exception { test6(); } public static void test1() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.nwn.test.Student"); byte[] bytes = cc.toBytecode(); System.out.println(Arrays.toString(bytes)); System.out.println(cc.getName());// cn.nwn.test.Student获取类名 System.out.println(cc.getSimpleName());// Student获取简要类名 System.out.println(cc.getSuperclass().getName());// java.lang.Object获取父类名 } public static void test2() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.nwn.test.Student"); CtMethod method1 = CtMethod.make("public int sum1(int a,int b){return a+b;}", cc); cc.addMethod(method1); CtMethod method2 = new CtMethod(CtClass.intType, "sum2", new CtClass[] { CtClass.intType, CtClass.intType }, cc); method2.setModifiers(Modifier.PUBLIC); method2.setBody("{return $1+$2;}");// 需要使用占位符 cc.addMethod(method2); Class cls = cc.toClass(); Object obj = cls.newInstance(); Method method = cls.getDeclaredMethod("sum2", int.class, int.class); Object result = method.invoke(obj, 200, 300); System.out.println(result); } public static void test3() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.nwn.test.Student"); CtMethod method1 = cc.getDeclaredMethod("sayHello", new CtClass[] { pool.get("java.lang.String") }); method1.insertBefore("System.out.println(\"before \"+$1);"); method1.insertAfter("System.out.println(\"after \"+$1);"); Class cls = cc.toClass(); Object obj = cls.newInstance(); Method method = cls.getDeclaredMethod("sayHello", String.class); method.invoke(obj, "hello");// setName没有返回值 } public static void test4() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.nwn.test.Student"); // CtField field1 = CtField.make("private int age;", cc); // cc.addField(field1); CtField field2 = new CtField(CtClass.intType, "age", cc); field2.setModifiers(Modifier.PRIVATE); cc.addField(field2); // cc.getDeclaredField("age"); cc.addMethod(CtNewMethod.getter("getAge", field2)); cc.addMethod(CtNewMethod.setter("setAge", field2)); } public static void test5() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.nwn.test.Student"); CtConstructor[] constructors = cc.getConstructors(); for (CtConstructor constructor : constructors) { System.out.println(constructor.getLongName()); } } public static void test6() throws Exception { CtClass cc = ClassPool.getDefault().get("cn.nwn.test.Student"); Object[] all = cc.getAnnotations(); Teacher teacher = (Teacher)all[0]; System.out.println(teacher.name()); } } @Teacher(name = "张三") class Student { private String name; public Student() { } public Student(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void sayHello(String word) { System.out.println(word); } } @Target(value = { ElementType.TYPE }) @Retention(value = RetentionPolicy.RUNTIME) @interface Teacher { String name(); }
局限性:jdk5.0新语法不支持(包括泛型、枚举);不支持注解修改,但可以通过底层的javassit类来解决;不支持数组的初始化,如String[]{”1”,”2”},除非数组容量为1;不支持内部类和匿名类;不支持continue和break表达式;对于继承关系,有些不支持
218、jvm核心机制
219、jvm核心机制_类加载
类加载机制:jvm把class文件加载到内存,并对数据进行校验,解析和初始化,最终形成jvm可以直接使用的java类型的过程
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时结构,在堆中生成一个代表这个类的Class对象,作为方法区类数据的访问入口。这个过程需要类加载器参与
链接:将java类的二进制代码合并到jvm的运行状态之中的过程
a) 验证:确保加载的类信息符合jvm规范,没有安全方面的问题
b) 准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
c) 解析:虚拟机常量池内的符号引用替换为直接引用的过程
初始化:执行类构造器clinit()方法的过程,类构造器clinit()方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的。当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先初始化其父类。虚拟机会保证一个类的clinit()方法在多线程环境中被正确加锁和同步。当访问一个java类的静态域时,只有真正声明为这个域的类才会被初始化
类的主动引用(一定会发生类的初始化)
new一个对象;调用静态成员(除了static final常量)和静态方法;使用java.lang.reflect包的方法对类进行反射调用;当虚拟机启动java HelloWorld则一定会初始化HelloWorld类(就是启动了main方法所在的泪);当初始化一个类,如果其父类没有被初始化,则先初始化其父类
类的被动引用(不会发生类的初始化)
当访问一个静态域时,只有真正声明这个域的类才会被初始化(子类引用父类静态变量,不会初始化子类);通过数组定义类的引用,不会触发此类的初始化;引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中(每个类都有常量池))
public class Test { public static void main(String[] args) { test1(); } public static void test1() { A a = new A();// 静态初始化类A(new一个对象) } public static void test2() { System.out.println(A.width);// 静态初始化类A(调用静态成员或静态方法会触发静态初始化) } public static void test3() { System.out.println(B.width);// 静态初始化类A(当访问一个静态域时,只有真正声明这个域的类才会被初始化) } public static void test4() { B b = new B();// 静态初始化类A、静态初始化类B、(代码块A)创建对象类A、(代码块B)创建对象类B(初始化一个类,如果其父类没有被初始化,则先初始化其父类) } public static void test5() { B b1 = new B(); B b2 = new B();// 静态初始化类A、静态初始化类B、(代码块A)创建对象类A、(代码块B)创建对象类B、(代码块A)创建对象类A、(代码块B)创建对象类B(静态初始化只有一次) } public static void test6() { System.out.println(A.height);// 引用常量不会触发此类的初始化 } public static void test7() { A[] a = new A[10];// 通过数组定义类的引用,不会触发此类的初始化 } } class A { public static int width = 100; public static final int height = 200; static { System.out.println("静态初始化类A"); } { System.out.println("代码块A"); } public A() { System.out.println("创建对象类A"); } } class B extends A { static { System.out.println("静态初始化类B"); } { System.out.println("代码块B"); } public B() { System.out.println("创建对象类B"); } }
这个加载过程 -> 首先(堆中)方法区内加载类Test的运行时数据(静态变量、静态方法、常量池(类名Test、数据类型等)、类的代码);同时在堆中加载代表Test的java.lang.Class对象,通过Class对象可以访问到Test类的结构;再者,方法区内加载类A的运行时数据,加载代表A的java.lang.Class对象,类加载完毕;执行main方法,在栈中形成main方法的栈帧(一个方法一个栈帧,方法调用方法则往里压),初始化a局部变量(一开始a的值为null),然后new A()则在堆里面生成A的对象,再把A对象的地址赋给局部变量a,a就拥有了A对象的地址
220、jvm核心机制_深入类加载器
类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口
类缓存:标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过,jvm垃圾收集器可以回收这些Class对象
类加载器的层次结构(树状结构)
引导类加载器:它用来加载java的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。加载扩展类和应用程序类加载器,并制定他们的父类加载器
扩展类加载器:用来加载java的扩展库,java虚拟机的实现会提供一个扩展库目录,该类加载器在此目录里面直接找并加载java类。由sun.misc.Launcher$ExtClassLoader实现
应用程序加载器:一般来说,java应用的类都是由它来文成加载,由sun.misc.Launcher$AppClassLoader实现
自定义类加载器:开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求
java.lang.ClassLoader类的基本职责就是根据一个制定的类的名称找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个java类,即java.lang.Class类的一个实例。除此之外,ClassLoader还负责加载java应用所需的资源,如图像文件和配置文件等
getParent()返回该类加载器的父类加载器
loadClass(String name)加载名称为name的类,返回结果是java.lang.Class类的实例
findClass(String name)查找名称为name的类,返回结果是java.lang.Class类的实例
findLoadedClass(String name)查找名称为name的已经被加载过的类,返回结果是java.lang.Class的实例
defineClass(String name,byte[] b,int off,int len)把字节数组b中的内容转换成java类,这个方法被声明为final,返回结果是java.lang.Class类的实例
resolveClass(Class<?> c)链接指定的java类
类加载器的代理模式
代理模式:交给其他加载器来加载指定的类
双亲委托机制:某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次追溯,知道最高级,如果父类加载器可以完成类加载任务,则成功返回,只有父类加载器无法完成此加载任务时,才可以自己加载(双亲委托机制是为了保证java核心库的类型安全;保证不会出现用户自己定义java.lang.Object类的情况,用户定义的java.lang.Object不会加载;类加载器除了用于加载类,也是安全的最基本保障)。双亲委托机制是代理模式的一种,并不是所有的类加载器都可以用双亲委托机制,tomcat服务器类加载器也使用代理模式,所不同的是它是首先尝试自己去加载某个类,如果找不到再代理给父类加载器
public class Test { public static void main(String[] args) { System.out.println(ClassLoader.getSystemClassLoader());// [email protected] System.out.println(ClassLoader.getSystemClassLoader().getParent());// [email protected] System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());// null顶层的引导类加载器不是java实现所以为null System.out.println(System.getProperty("java.class.path")); String str = "abc"; System.out.println(str.getClass().getClassLoader());// null } }
221、jvm核心机制_深入类加载
自定义类加载器的流程:继承java.lang.ClassLoader;首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载则直接返回;委派类加载请求给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例;试图获取对应的字节码,如果获取到则调用defineClass()导入类型到方法区;如果获取不到对应的字节码或其他原因失败,抛出ClassNotFoundException异常。注意:被两个类加载器加载的同一个类,jvm不认为是相同的类
public class Test { public static void main(String[] args) throws ClassNotFoundException { FileSystemClassLoader loader1 = new FileSystemClassLoader("d:/tst"); Class<?> c1 = loader1.loadClass("cn.nwn.test.Student"); FileSystemClassLoader loader2 = new FileSystemClassLoader("d:/tst"); Class<?> c2 = loader2.loadClass("cn.nwn.test.Student"); Class<?> c3 = loader1.loadClass("cn.nwn.test.Student"); System.out.println(c1 == c2);// false两个类加载器加载同一个类,jvm不认为是相同的类 System.out.println(c1 == c3);// true System.out.println(c1.getClassLoader()); System.out.println(c2.getClassLoader()); System.out.println(c3.getClassLoader()); } } class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir) { super(); this.rootDir = rootDir; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> c = findLoadedClass(name);// 检查是否已经被加载过,是则直接返回 if (null != c) { return c; } else { ClassLoader parent = this.getParent();// [email protected] try { c = parent.loadClass(name);// (双亲委派机制)委派给父类加载器看是否能完成加载(此处加入try是为了避免异常让程序往下走) } catch (Exception e) { // e.printStackTrace(); } if (null != c) { return c; } else { byte[] classData = getClassData(name); if (null == classData) { throw new ClassNotFoundException(); } else { c = defineClass(name, classData, 0, classData.length); } } } return c; } private byte[] getClassData(String name) { String path = rootDir + "/" + name.replace(".", "/") + ".class"; InputStream is = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { is = new FileInputStream(path); byte[] buffer = new byte[1024]; int len = 0; while (-1 != (len = is.read(buffer, 0, buffer.length))) { baos.write(buffer, 0, len); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if (null != baos) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
222、jvm核心机制_深入类加载
public class Test { public static void main(String[] args) throws ClassNotFoundException { Encrpt.encrpt("d:/tst/cn/nwn/test/Student.class", "d:/tst/tmp/cn/nwn/test/Student.class"); DecrptClassLoader loader = new DecrptClassLoader("d:/tst/tmp"); Class<?> c = loader.loadClass("cn.nwn.test.Student"); System.out.println(c); } } class DecrptClassLoader extends ClassLoader { private String rootDir; public DecrptClassLoader(String rootDir) { super(); this.rootDir = rootDir; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> c = findLoadedClass(name);// 检查是否已经被加载过,是则直接返回 if (null != c) { return c; } else { ClassLoader parent = this.getParent();// [email protected] try { c = parent.loadClass(name);// (双亲委派机制)委派给父类加载器看是否能完成加载(此处加入try是为了避免异常让程序往下走) } catch (Exception e) { // e.printStackTrace(); } if (null != c) { return c; } else { byte[] classData = getClassData(name); if (null == classData) { throw new ClassNotFoundException(); } else { c = defineClass(name, classData, 0, classData.length); } } } return c; } private byte[] getClassData(String name) { String path = rootDir + "/" + name.replace(".", "/") + ".class"; System.out.println(path); InputStream is = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { is = new FileInputStream(path); int tmp = -1; while (-1 != (tmp = is.read())) { baos.write(tmp ^ 0xff); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if (null != baos) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } } class Encrpt { public static void encrpt(String src, String dest) { FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream(src); fos = new FileOutputStream(dest); int tmp = -1; while (-1 != (tmp = fis.read())) { fos.write(tmp ^ 0xff);// 按位取反 } } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != fos) { fos.close(); } } catch (IOException e1) { e1.printStackTrace(); } try { if (null != fis) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
223、jvm核心机制_线程上下文
双亲委托机制以及类加载器的问题
一般情况下,保证同一个类中所关联的其他类都是当前类的类加载器所加载的,比如ClassA本身在Ext下找到,那么它里面new出来的一些类也就只能用Ext去查找了(不能低一个级别),所以有些明明App可以找到的,却找不到了
JDBC API,它有实现的driver部分,我们的JDBC API都是由Boot或者Ext来加载的,但是JDBC driver确是由Ext或者App来载入,那么就有可能找不到driver了。在java领域中,其实只要分成API+SPI(特定厂商提供的),都会遇到此问题
常见的SPI有JDBC、JCE、JNDI、JAXP和JBI等,这些SPI的接口由java核心库来提供,如JAXP的SPI接口定义包含在javax.xml.parsers包中。SPI的接口是java核心库的一部分,是由引导类加载器来加载的,SPI实现的java类一般是由系统类加载器加载,引导类加载器是无法找到SPI的实现类的,因为它只加载java的核心库
通常当你需要动态加载资源时,至少有三个ClassLoader可以选择(系统类加载器或叫作应用类加载器、当前类加载器、当前线程类加载器)
线程类加载器是为了抛弃双亲委派加载链模式。每个线程都有一个关联的上下文类加载器,如果你使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文类加载器。如果程序对线程上下文类加载器没有任何改动的话,程序中所有的线程将使用系统类加载器作为上下文类加载器
Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader();
public class Test { public static void main(String[] args) { ClassLoader loader1 = Test.class.getClassLoader(); System.out.println(loader1);// [email protected] ClassLoader loader2 = Thread.currentThread().getContextClassLoader(); System.out.println(loader2);// [email protected] Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("d:/tst")); System.out.println(Thread.currentThread().getContextClassLoader());// [email protected] } } class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir) { super(); this.rootDir = rootDir; } }
tomcat服务器的类加载机制:一切都是为了安全。tomcat不能使用系统默认的类加载器,如果tomcat跑你的web项目使用系统的类加载器那是相当危险的,你可以直接肆无忌惮地操作系统的各个目录了。对于运行在java EE容器中的web应用来说,类加载器的实现方式与一般的java应用有所不同。每个web应用都有一个对应的类加载器实例,该类加载器也使用代理模式(不同于前面说的双亲委托机制),所不同的是它是首先尝试加载某个类,如果找不到再代理给父类加载器,这与一般类加载器的顺序是相反的,但也是为了保证安全,这样核心库就不在查询的范围之内。为了安全tomcat需要实现自己的类加载器(可以限制只能把类写在制定的地方,否则不给加载)
OSGI:是java上的动态模块系统,它为开发人员提供了面向服务和基于组件的运行环境,并提供标准的方式用来管理软件的生命周期
OSGI原理:OSGI中的每个模块都包含java包和类,模块可以声明它所依赖的需要导入(import)的其他模块的java包和类(Import-Package),也可以声明导出(export)自己的包和类,供其它模块使用(Export-Package),也就是说需要能够隐藏和共享一个模块中的某些java包和类。这是通过OSGI特有的类加载器机制来实现的。OSGI中的每个模块都有对应的一个类加载器,它负责加载模块自己包含的java包和类,当它需要加载java核心库的类时,它会代理给父类加载器(通常是启动类加载器)来完成。当它需要加载所导入的java类是,它会代理给导出此java类的模块来完成加载,模块也可以显式的声明某些java包和类,必须由父类加载器来加载。只需要设置系统属性org.osgi.framework.bootdelegation的值即可。
224、内部分类介绍_静态内部类
225、方法内部类_final修饰局部变量
内部类(Nested Class):又称嵌套类,分为静态内部类和非静态内部类(普通内部类(成员内部类)(在外部类内直接定义)、匿名内部类、方法内部类(在一个外部类方法或代码块中定义的内部类))
内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的class文件,但是前面冠以外部类的类名和$符号
内部类可以使用修饰符(public、private、default、protected)
public class Test { /* 修饰符 */ class InnerClass {// 成员内部类 } /* 修饰符 */ static class StaticInnerClass {// 静态内部类 } Runnable runnable1 = new Runnable() {// 匿名内部类(同时也是成员内部类) @Override public void run() { }; }; public void test() { /* 修饰符 */ class MethodInnerClass {// 方法内部类 } Runnable runnable2 = new Runnable() {// 匿名内部类(同时也是方法内部类) @Override public void run() { }; }; } }
静态内部类
可以包含静态成员、非静态成员
可以直接调用外部类的静态属性、静态方法,但不能调用外部类的普通属性、普通方法
在不相关类中,可以直接创建静态内部类的对象(不需要通过外部类对象)
静态内部类实际上和外部类联系很少,也就是命名空间上的联系
public class Test { public static void main(String[] args) { Temp.StaticInnerClass innerCls = new Temp.StaticInnerClass();// 在不相关类中,可以直接创建静态内部类的对象 } } class Temp { private int a = 0; private static int b = 0; void testA() { } static void testB() { } /* 修饰符 */ static class StaticInnerClass {// 静态内部类 int s = 0; static int p = 0;// 静态内部类可以包含静态成员和非静态成员 void method() { // System.out.println(a); // testA();// 静态内部类不能调用外部类的普通属性、普通方法 System.out.println(b); testB();// 静态内部类只能调用外部类的静态属性、静态方法 } } }
成员内部类(普通内部类)
成员内部类就像一个成员变量一样存在于外部类中;可以访问外部类的所有成员(包括private的);成员内部类的this指向内部类对象本身,要拿到外部类对象可以使用:外部类名.this(成员内部类的对象是一定要绑定到一个外部类对象上的,因此,创建成员内部类对象时需要持有外部类对象的引用,因此需要先有外部类对象,后有成员内部类对象);成员内部类不能有静态成员
成员内部类创建方式:Inner inner = new Outer().net Inner();
public class Test { public static void main(String[] args) { Outer.Inner inner = new Outer().new Inner(); inner.innerTest(); } } class Outer { private int a = 0; private static int b = 0; class Inner { // private static int i = 0; // static void inner() { // } private static final int j = 0;// 成员内部类不能有静态属性(final常量除外)、静态方法 void innerTest() { System.out.println(a); System.out.println(b);// 成员内部类可以访问外部类的所有成员 System.out.println(this);// 内部类对象 System.out.println(Outer.this);// 外部类对象 } } }
方法内部类(局部内部类)
方法内部类的地位和方法内的局部变量的位置类似,因此不能修饰局部变量的修饰符也不能修饰局部内部类;方法内部类只能在声明的方法内是可见的,因此定义局部内部类之后,想用的话就要在次方法内直接实例化,必须先声明后实例化;方法内部类不能方位定义它的方法内的局部变量,final除外(局部变量和方法内部类生命周期不一致);方法内部类只能包含非静态成员
public class Test { public static void main(String[] args) { new Test().test(); } public void test() { class InnerClass {// 方法内部类不能修饰符修饰 int i = 0; static final int j = 0; // static int k = 0;// 方法内部类只能定义非静态成员 public void innerTest() { } } new InnerClass().innerTest(); } }
匿名内部类
匿名内部类的实例化方式:new InterfaceOrClass(){};
根据声明的位置,判断匿名内部类是成员内部类还是方法内部类(一般是方法内部类)
public class Test { public static void main(String[] args) { Runnable runnable = new Runnable(){ @Override public void run() { } }; } } public class Test { public static void main(String[] args) { Car car = new Car() {// Car的匿名子类 @Override void go() { System.out.println("Car的匿名子类go"); } }; car.go(); } } class Car { void go() { System.out.println("Car go"); } }