Java基础-开篇
之前在新浪博客写了不少springmvc的相关技术,但新浪博客毕竟不是专业的技术博客,添加代码很不方便,就开始在博客园试试了。
使用java开发也不少年了,准备再次整理一些java基础知识,当然,这次不仅仅是了解一些概念,更希望能对基础知识进行更深入的原理分析。
————————————————————————————————————————————————————————————————
以上是题外话。
学习java都会接触3个常见的名称:JDK、JRE、JVM。
来看看它们都是什么:
package com.yun.jvm;
/**
* Base Knowledge
* @author Mars
*
*/
public class BaseKnowledge {
private static final String LINE_SEPARATOR = "line.separator";
/**
* Java Development Kit
* java的开发工具,包括jre+开发工具
*/
private String jdk =
"JDK:" + System.getProperty(LINE_SEPARATOR)
+ "Java Development Kit;" + System.getProperty(LINE_SEPARATOR)
+ "java的开发工具,包括jre+开发工具" + System.getProperty(LINE_SEPARATOR);
/**
* Java Runtime Environment
* java的运行环境,包括jvm+java的核心类库
*/
private String jre =
"JRE:" + System.getProperty(LINE_SEPARATOR)
+ "Java Runtime Environment;" + System.getProperty(LINE_SEPARATOR)
+ "java的运行环境,包括jvm+java的核心类库" + System.getProperty(LINE_SEPARATOR);
/**
* Java Virtual Machine
* java虚拟机,用于保证java的跨平台的特性
* Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,
* 使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),
* 就可以在多种平台上不加修改地运行。
* Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行
*/
private String jvm =
"JVM:" + System.getProperty(LINE_SEPARATOR)
+ "Java Virtual Machine;" + System.getProperty(LINE_SEPARATOR)
+ "java虚拟机,用于保证java的跨平台的特性;" + System.getProperty(LINE_SEPARATOR)
+ "Java语言使用Java虚拟机屏蔽了与具体平台相关的信息," + System.getProperty(LINE_SEPARATOR)
+ "使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码)," + System.getProperty(LINE_SEPARATOR)
+ "就可以在多种平台上不加修改地运行。" + System.getProperty(LINE_SEPARATOR)
+ "Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行" + System.getProperty(LINE_SEPARATOR);
public String getJdk() {
return jdk;
}
public void setJdk(String jdk) {
this.jdk = jdk;
}
public String getJre() {
return jre;
}
public void setJre(String jre) {
this.jre = jre;
}
public String getJvm() {
return jvm;
}
public void setJvm(String jvm) {
this.jvm = jvm;
}
}
使用代码的方式来展示是因为程序员是需要在不断的写代码中来提升的,只看技术性的文字,就没有独属于程序员的亲切感,印象不深。
BaseKnowledge.java是一个标准的java式的面向对象写法,变量私有化,通过get、set方法来访问。
现在的java开发工具(eclipse等)都具有自动生成get、set方法的功能。想起自己刚毕业参加工作时,还不熟悉开发工具,全部手写get、set方法,当私有变量很多时,可以想象那个画面。
这段代码很简单,什么都没做,就是在代码里介绍了JDK、JRE、JVM是什么。在这里只分析一下其中一句代码:
System.getProperty(LINE_SEPARATOR)
这句代码是从系统中获取当前操作系统的换行符,不同操作系统的换行符是不一样的,比如windows系统是/r/n,linux系统是/n,苹果机系统是/r。
使用这句代码可以避免硬编码带来的不同操作系统可能产生的问题,也省去了手动进行不同操作系统的分支判断。
public static String getProperty(String key) {
checkKey(key);
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertyAccess(key);
}
return props.getProperty(key);
}
这个方法属于java.lang.System类。
1.checkKey方法,判断key是否为空值,如果是空值,直接抛出异常。
private static void checkKey(String key) {
if (key == null) {
throw new NullPointerException("key can't be null");
}
if (key.equals("")) {
throw new IllegalArgumentException("key can't be empty");
}
}
注意这里的逻辑处理方式,是通过抛异常的方式来处理key值为空的情况,而不是另外一种boolean判断方法:
private static boolean checkKey(String key) {
if (key == null || key.equals("")) {
return false;
}
return true;
}
public static String getProperty(String key) {
if (checkKey(key)) {
// do sth..
}
return null;
}
个人觉得很多时候程序员写代码都对java的异常处理Exception关注不够,笔者参与的很多项目也没有对Exception有比较规范成体系的处理。
这两种写法在具体的项目开发中,值得去思考一下如何选择。
2.获取SecurityManager,校验当前的key在操作系统安全策略里是不是可访问的。
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertyAccess(key);
}
一层层向下看:
public void checkPropertyAccess(String key) {
checkPermission(new PropertyPermission(key,
SecurityConstants.PROPERTY_READ_ACTION));
}
public void checkPermission(Permission perm) {
java.security.AccessController.checkPermission(perm);
}
public static void checkPermission(Permission perm)
throws AccessControlException
{
//System.err.println("checkPermission "+perm);
//Thread.currentThread().dumpStack();
if (perm == null) {
throw new NullPointerException("permission can't be null");
}
AccessControlContext stack = getStackAccessControlContext();
// if context is null, we had privileged system code on the stack.
if (stack == null) {
Debug debug = AccessControlContext.getDebug();
boolean dumpDebug = false;
if (debug != null) {
dumpDebug = !Debug.isOn("codebase=");
dumpDebug &= !Debug.isOn("permission=") ||
Debug.isOn("permission=" + perm.getClass().getCanonicalName());
}
if (dumpDebug && Debug.isOn("stack")) {
Thread.dumpStack();
}
if (dumpDebug && Debug.isOn("domain")) {
debug.println("domain (context is null)");
}
if (dumpDebug) {
debug.println("access allowed "+perm);
}
return;
}
AccessControlContext acc = stack.optimize();
acc.checkPermission(perm);
}
public void checkPermission(Permission perm)
throws AccessControlException
{
boolean dumpDebug = false;
if (perm == null) {
throw new NullPointerException("permission can't be null");
}
if (getDebug() != null) {
// If "codebase" is not specified, we dump the info by default.
dumpDebug = !Debug.isOn("codebase=");
if (!dumpDebug) {
// If "codebase" is specified, only dump if the specified code
// value is in the stack.
for (int i = 0; context != null && i < context.length; i++) {
if (context[i].getCodeSource() != null &&
context[i].getCodeSource().getLocation() != null &&
Debug.isOn("codebase=" + context[i].getCodeSource().getLocation().toString())) {
dumpDebug = true;
break;
}
}
}
dumpDebug &= !Debug.isOn("permission=") ||
Debug.isOn("permission=" + perm.getClass().getCanonicalName());
if (dumpDebug && Debug.isOn("stack")) {
Thread.dumpStack();
}
if (dumpDebug && Debug.isOn("domain")) {
if (context == null) {
debug.println("domain (context is null)");
} else {
for (int i=0; i< context.length; i++) {
debug.println("domain "+i+" "+context[i]);
}
}
}
}
/*
* iterate through the ProtectionDomains in the context.
* Stop at the first one that doesn't allow the
* requested permission (throwing an exception).
*
*/
/* if ctxt is null, all we had on the stack were system domains,
or the first domain was a Privileged system domain. This
is to make the common case for system code very fast */
if (context == null) {
checkPermission2(perm);
return;
}
for (int i=0; i< context.length; i++) {
if (context[i] != null && !context[i].implies(perm)) {
if (dumpDebug) {
debug.println("access denied " + perm);
}
if (Debug.isOn("failure") && debug != null) {
// Want to make sure this is always displayed for failure,
// but do not want to display again if already displayed
// above.
if (!dumpDebug) {
debug.println("access denied " + perm);
}
Thread.dumpStack();
final ProtectionDomain pd = context[i];
final Debug db = debug;
AccessController.doPrivileged (new PrivilegedAction<Void>() {
public Void run() {
db.println("domain that failed "+pd);
return null;
}
});
}
throw new AccessControlException("access denied "+perm, perm);
}
}
// allow if all of them allowed access
if (dumpDebug) {
debug.println("access allowed "+perm);
}
checkPermission2(perm);
}
private void checkPermission2(Permission perm) {
if (!isLimited) {
return;
}
/*
* Check the doPrivileged() context parameter, if present.
*/
if (privilegedContext != null) {
privilegedContext.checkPermission2(perm);
}
/*
* Ignore the limited permissions and parent fields of a wrapper
* context since they were already carried down into the unwrapped
* context.
*/
if (isWrapped) {
return;
}
/*
* Try to match any limited privilege scope.
*/
if (permissions != null) {
Class<?> permClass = perm.getClass();
for (int i=0; i < permissions.length; i++) {
Permission limit = permissions[i];
if (limit.getClass().equals(permClass) && limit.implies(perm)) {
return;
}
}
}
/*
* Check the limited privilege scope up the call stack or the inherited
* parent thread call stack of this ACC.
*/
if (parent != null) {
/*
* As an optimization, if the parent context is the inherited call
* stack context from a parent thread then checking the protection
* domains of the parent context is redundant since they have
* already been merged into the child thread's context by
* optimize(). When parent is set to an inherited context this
* context was not directly created by a limited scope
* doPrivileged() and it does not have its own limited permissions.
*/
if (permissions == null) {
parent.checkPermission2(perm);
} else {
parent.checkPermission(perm);
}
}
}
这里不做更细致的分析,只是简单的贴出代码,有兴趣的可以细看。
有两点可以注意一下:
一是checkPermission方法里对于没有访问权限的key,同样是通过抛出异常的方式来处理,而不是返回true or false;
二是有个checkPermission2的方法,按理说这种命名方式是不可取的,不符合命名规范,不知道是不是因为代码是反编译出来的原因。
(注:贴出的代码属于java.security.AccessController和java.security.AccessControlContext类)
3.经过非空判断和访问权限校验后,从props里获取具体的key对应的value
return props.getProperty(key);
props是System类的成员变量,会在JVM启动时进行初始化:
private static void initializeSystemClass() {
// VM might invoke JNU_NewStringPlatform() to set those encoding
// sensitive properties (user.home, user.name, boot.class.path, etc.)
// during "props" initialization, in which it may need access, via
// System.getProperty(), to the related system encoding property that
// have been initialized (put into "props") at early stage of the
// initialization. So make sure the "props" is available at the
// very beginning of the initialization and all system properties to
// be put into it directly.
props = new Properties();
initProperties(props); // initialized by the VM
private static native Properties initProperties(Properties props);
initProperties是一个本地(native)方法。
System.getProperty()方法就分析到这里了,可以看出,处处都是知识点啊,很简单的代码,它背后的东西往往不简单;当然,如果细细的分析清楚了,那么再复杂的代码,在程序员眼里,也会很简单。
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
分割线。
接下来准备对java程序运行的根本,JVM,进行比较详细的分析。弄清楚JVM的原理,对我们写代码和程序优化,都会有非常大的帮助。
——————————————————————————————————————————————————————————————————
package com.yun.jvm; /** * Main * @author Mars * */ public class Main { /** * main method * @param args */ public static void main(String[] args) { // baseKnowledge BaseKnowledge baseKnowledge = new BaseKnowledge(); // jdk String jdk = baseKnowledge.getJdk(); // jre String jre = baseKnowledge.getJre(); // jvm String jvm = baseKnowledge.getJvm(); // output System.out.println("My dear, my wife, I love you !"); System.out.println(jdk); System.out.println(jre); System.out.println(jvm); } }