Base64是一种数据表示方法,它使用64个可打印字符来表示二进制数据,所以才叫做Base64。
Base64一般用于处理文本数据,表示、传输、存储。比如MIME中的email,以及在xml中存储复杂数据。
编码
将普通数据转换成符合Base64标准的数据的过程。
解码
将Base64标准的数据转换成普通数据的过程。
52个字母:大写字母+小写字母
10个数字:0~9
两个特殊字符:+ 和 /
以上就够64个了,但是有时候还会用一个“=”,等号的作用是填充,后面会讲到。
Base64字符对照表:
数值 | 字符 | 数值 | 字符 | 数值 | 字符 | 数值 | 字符 |
---|---|---|---|---|---|---|---|
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | + |
15 | P | 31 |
在java中,long和double类型的数据都占8个字节长度,即64位。对于long和double类型的读写操作,java并不能保证其原子性。下面的内容来自Java Language Specification:
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.
Writes and reads of volatile long and double values are always atomic.
Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency’s sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to pe
在Java中提供了对于线程设置ContextClassLoader的方法,关于上下文类加载器,下面摘抄的内容将的比较明白:
线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。
前面提到的类加载器的代理模式并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代码经常需要加载具体的实现类。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory类中的 newInstance()方法用来生成一个新的 DocumentBuilderFactory的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apa
思考:如果允许用户输入java代码,然后在程序中编译执行,该如何做?
其实这可以通过扩展Java Compiler API来实现。
假设我们要实现一个计算器,给定两个参数,根据不同的算法计算结果,而算法的实现是允许用户手动输入的,用户输入的算法不同,计算结果也不同。
首先我们需要定义一个Function接口,代码如下:
public interface Function {
public int calculate(int x, int y); //为了简单,这里参数与返回值都是int类型
}
java程序执行之前,需要将java源代码编译成class文件,然后将class文件通过ClassLoader加载到JVM虚拟机,然后创建对应类对象才能执行。同样道理,用户输入的字符串也需要经过编译,加载才能运行。所以我们也需要两步:1.编译代码 2.通过ClassLoader加载实现类
动态编译需要使用Java Compiler API,我们首先自定义一个编译器,实现编译字符串源码的功能。
下面是一个完整实现:
public Class<?> compile(String classFullName, String sourceCode) throws Exception
方法需要两个参数,第一个是完整的类名,第二个是对应的源码。该方法执行了两个过程:1.编译2.loadClass,返回值为对应的Class实例
过程中需要自定义ClassLoader:ClassLoaderImpl
,自定义JavaObjectFile:JavaObjectFileImpl
,自定义FileManager:FileManagerImpl
package com.
在研究Dubbo源码的时候发现了Dubbo提供的Adaptive机制是通过动态生成java代码,然后编译返回Class类实例的方式实现的,研究发现使用的是java提供的compiler API,于是决定先研究下这个API,以实现动态编译java类并返回java Class实例功能。
JSR199提出了Java Compiler API来提供一种编译java代码的标准方式。java compiler API存在于javax.tools包中。它允许客户端通过程序定位并执行编译器,从而实现动态编译java代码。
定义一个需要被动态编译的类:
public class HelloWorld {
public void print() {
System.out.println("hello world!");
}
}
通过JavaCompiler进行编译:
public static void main(String[] args) throws FileNotFoundException {
// 获取系统的java编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 指定要编译的java文件
String fileToCompile = "D:/eclipse_workspace/compile/src/main/java/com/medusar/compile/HelloWorld.java";
// 执行编译方法
int compilationResult = compiler.run(null, null, null, fileToCompile);
// 返回0表示编译成功
if (compilationResult == 0) {
System.out.pr