标签 - java

设计模式 java    2016-03-16 19:01:09    672    0    0

相同点

  1. 简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。
  2. 主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。

不同点

类图

简单工厂:
title
工厂方法:
title
抽象工厂:
title

应用场景

简单工厂:适合创建同一级别的不同对象。
工厂方法:为每种产品提供一个工厂类,通过不同的工厂实例来创建不同的产品。工厂方法模式中我们把生成产品类的时间延迟,就是通过对应的工厂类来生成对应的产品类,在这里我们就可以实现“开发-封闭”原则,无论加多少产品类,我们都不用修改原来类中的代码,而是通过增加工厂类来实现。但是这还是有缺点的,如果产品类过多,我们就要生成很多的工厂类。
抽象工厂:针对产品族,比如每个汽车公司都要同时生产轿车,货车,客车,那么每一个工厂都要有对应的生产轿车,货车和客车的方法。所以,抽象工厂类中一般会有多个抽象方法。什么是产品族呢?简单的理解就是,不同牌子产的车里面会有跑车类型,家庭类型,商用类型等的车,不同牌子的车的跑车类型的车可以组成一个产品族。抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类,抽象工厂模式较好的实现了“开放-封闭”原则,是三个模式中较为抽象,并具一般性的模式。

业务变更的差别

下面当我们看一下,需要增加产品的时候,三种模式都需要如何操作。
简单工厂:如果是新增同一类型的产品(都是实现Product接口的),那么需要在工厂类的ifelse中新增一个判断。客户端不需要做变更
工厂方法:新增一个产品,我们就需要新增加一个工厂类。客户端也需要变更。
抽象工厂:新增一类产品,比如新增跑车,那么需要修改抽象工厂以及所有的实现类,改动很大。新增某一个产品,比如新增一款颜色的轿车,也需要单独的工厂实现类。

简单工厂 :用来生产同一等级结构中的任意产品。(对于增加新的产品,无能为力)
工厂方法 :用来生产同一等级结构中的固定产品。(支持增加任意产品)
抽象工厂 :用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)

JDK中的应用

简单工厂在JDK中的应用有很多:

java.te
linux java    2016-03-06 11:13:53    518    0    0

Linux中查找Java位置

whereis java  # 查找java文件
which java # 查看java命令执行路径
echo $JAVA_HOME #打印JAVA_HOME环境变量
echo $PATH 

Linux中修改环境变量

修改全局文件(所有用户都生效)

修改/etc/profile文件

vi 此文件/etc/profile
在profile文件末尾加入:
export JAVA_HOME=/usr/share/jdk1.6.0_20
export PATH=JAVAHOME/bin:PATH
export CLASSPATH=.:JAVAHOME/lib/dt.jar:JAVA_HOME/lib/tools.jar

注意:上面的/usr/share/jdk1.6.0_20是JDK安装路径,可以通过which java查看。
修改完profile文件之后还没有生效,想要生效需要执行source profile命令。

修改.bash_profile文件 (某个用户权限使用这些环境变量)

.bash_profile文件末尾加入:
export JAVA_HOME=/usr/share/jdk1.6.0_20
export PATH=JAVAHOME/bin:PATH
export CLASSPATH=.:

java 架构 模式    2016-02-28 13:08:30    1383    1    0

流水线

什么是流水线?

在计算机中,对于一条具体的指令执行过程,通常可以分为五个部分:取指令,指令译码,取操作数,运算 (ALU),写结果。
前三步由指令控制器完成,后两步则由运算器完成。
按照传统的方式,所有指令顺序执行,那么先是指令控制器工作,完成第一条指令的前三步,然后运算器工作,完成后两步,第一条指令执行完毕。然后第二条指令又是先指令控制器工作,完成前三步,然后运算器,完成第二条指令的后两部……
传统方式有个很大的缺点就是,指令只能一条一条地执行,仔细分析一下就会发现,这种方式存在很大的资源浪费:即同一时刻,要么指令控制器工作,运算器闲着;要么运算器工作,指令控制器闲着。这样一方面资源得不到有效利用,另一方面就是工作效率很低。
流水线的出现就是为了解决这个问题,下面我们来看一下流水线的工作模式:
假设有两个指令INS_A和INS_B,它们的执行分别要经过A,B,C,D四个过程,假设A到D四个过程分别由四个硬件元件完成。按照传统的方式,它们的流程如下:
传统方式
这种方式的缺点就是,只能一条指令一条指令的执行,并且当指令执行到过程B的时候,处理过程A和CD的元件是处于空闲状态的。

流水线方式如下:
流水线方式

说明一下,通过流水线的方式,当INS_A指令执行完过程A之后,处理过程A的元件就空闲了,此时我们就开始处理指令INS_B的A阶段,这样一来,INS_B指令只需要等到INS_A的A过程执行完成就可以继续执行了,这样以来就在很大程度上提高了效率。

流水线中断

使用流水线能够很大程度提高程序执行效率,这点是毋庸置疑的,但是,在系统中,每当引入一个新的模式或者组件的时候,我们就需要对应处理该模式或者组件所带来的问题,那么引入流水线的一个很大的问题就是流水线中断。

产生中断一般是由两个原因造成的,一个是“相关”,一个是“条件转移”。
相关:指的是一条指令的执行需要依赖前一条指令的执行结果。拿上面的例子,假设INS_B指令在执行过程C的时候,需要使用INS_A指令过程D的结果,那么指令INS_B在执行到C的时候,由于A指令的D过程还没有执行完成,所以此时INS_B就不能继续执行,否则就会拿到错误结果。所以此时就需要将流水线中断

java 异常    2016-02-23 20:03:57    1151    0    0

使用Dom4j解析xml文件的时候报错如下:

Exception in thread "main" java.lang.NoClassDefFoundError: org/jaxen/JaxenException
    at org.dom4j.DocumentFactory.createXPath(DocumentFactory.java:230)
    at org.dom4j.tree.AbstractNode.createXPath(AbstractNode.java:207)
    at org.dom4j.tree.AbstractNode.selectSingleNode(AbstractNode.java:183)
    at xml.XmlReader.main(XmlReader.java:29)
Caused by: java.lang.ClassNotFoundException: org.jaxen.JaxenException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 4 more

程序代码如下:

InputStream stream = XmlRead
java 调优 JDK    2016-02-11 16:48:06    211    0    0

JPS命令

列出Java进程,常用命令jps -l可以显示当前系统中运行的java进程I等信息

查看当前系统中的java进程

jason@jason-Lenovo-V3000:~$ jps -l
6200 sun.tools.jps.Jps
5451 test.HttpServerTest
3055 /opt/eclipse//plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar

jstat命令

可以用来查看堆信息,垃圾回收信息等,详细使用命令可以通过man jstat命令来查看。

查看垃圾回收信息

jason@jason-Lenovo-V3000:~$ jstat -gc 5451
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
5120.0 5120.0  0.0    0.0   31744.0   1270.1   84992.0      0.0     4480.0 773.8  384.0   75.8       0    0.000   0      0.000    0.000
 S0C: Current survivor space 0 capacity (kB).

           S1C: Current survivor space 1 capacity (kB).

           S0U: Survivor space 0 utilization (kB).

           S1U: Survivor space 1 utilization (kB).

           EC: Current eden space capacity (kB).

           EU: Eden space utilization (kB).

           OC:
java 最佳实践    2016-02-11 15:45:19    297    0    0

很多东西会用兵不一定能用的好,虽然有时候自己灵光一闪,有一个好的想法,但是过去很久之后可能就会忘记,所以,还是把自己学到的一些技巧记录下来,总结一下

异常

适当扩大try-catch范围

例如下面的代码:

for (int i = 0; i < 10000; i++) {
            try {
                // do something..
            } catch (Exception e) {
                //handle exception
            }
        }

可以通过扩大异常捕获范围,即将try-catch拿到for循环外边来,可以在一定程度上提高性能。

变量

使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中,速度比较快。其他变量,如静态变量,实例变量等,都在堆中创建,速度相对较慢。

int a =0;
for (int i = 0; i < 10000; i++) {
            try {
                // do something..
            } catch (Exception e) {
                //handle exception
            }
        }

比下面代码要快

public static int ta =0;
    for (int i = 0; i < 1000000; i++) {
        ta++;
    }

位运算代替乘除法

通过位运算来代替乘除法,位运算仅仅通过左移或者右移就可以完成。
a*=2可以替换成a<<=1,a/=2可以替换成a>>=1

巧用数组替换判断及switch

例如下面的代码,用了很多switch判断:

public static void main(String[] args) {
        int ret=0;
        for (int i = 0; i < 1000000
java 基础    2016-02-10 10:26:37    602    0    0

long和double类型

在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 基础    2016-02-06 19:06:38    3859    0    0

Thread.setContextClassLoader(ClassLoader cl)

在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 javassist    2016-02-06 11:58:34    518    0    0
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

import org.junit.Test;

import com.medusar.compile.Hello;

/**
 * Date: 2016年2月5日 下午3:30:54 <br/>
 * 
 * javassist文档:
 * https://jboss-javassist.github.io/javassist/tutorial/tutorial.html
 * 
 */
public class JavassistTest {

    @Test
    public void testNewInstance() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get("com.medusar.compile.Hello");
        Class clzz = ctClass.toClass();
        Object obj = clzz.newInstance();
        Hello hello = (Hello) obj;
        hello.print();
    }

    @Test
    public void testSetSuperClass() throws Exception, Throwable {
        ClassPool pool = ClassPool.get
java 基础 compilerAPI    2016-02-05 13:54:08    707    0    0

思考:如果允许用户输入java代码,然后在程序中编译执行,该如何做?

其实这可以通过扩展Java Compiler API来实现。

问题引入

假设我们要实现一个计算器,给定两个参数,根据不同的算法计算结果,而算法的实现是允许用户手动输入的,用户输入的算法不同,计算结果也不同。

实现思路

  1. 首先我们需要定义一个算法接口Function,它提供一个calculate(int x,int y)方法,返回x和y的计算结果。
  2. 用户通过手动输入代码来提供Function接口的具体实现类
  3. 用户输入完成之后,程序自动加载用户输入的代码并执行,然后将执行结果输出

具体实现

Function接口

首先我们需要定义一个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.