8

性能调优读书笔记(上篇) - 女友在高考

 2 years ago
source link: https://www.cnblogs.com/javammc/p/16637682.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、Amdahl定律

加速=优化前耗时/优化后耗时比
公式图:

1178991-20220829223713136-425842631.jpg

二、设计模式

1、单例模式

静态内部类的方式:

/**
 * 内部类的单例模式
 */
public class StaticSingleton {
    private StaticSingleton(){
        System.out.println("aaa");
    }

    private static class StaticSingletonHolder{
       private static StaticSingleton singleton=new StaticSingleton();
    }

    public static StaticSingleton getInstance(){
        return StaticSingletonHolder.singleton;
    }
}

除了反射机制强制调用私有构造函数,生成多个实例外,序列化和反序列化也可能会导致。

防止序列化的单例:

/**
 * 可以被串行化的单例
 */
public class SerSingleton implements Serializable {

    String name;

    private SerSingleton(){
        System.out.println("SerSingleton is create");
        name="SerSingleton";
    }

    private static SerSingleton instance=new SerSingleton();

    public static SerSingleton getInstance(){
        return instance;
    }

    public static void createString(){
        System.out.println("createString in Singleton");
    }

    //阻止生成新的实例,总是返回当前对象
    private Object readResolve(){
        return instance;
    }
}

关键是实现了readResolve方法

2、代理模式

1、代理模式用于延迟加载

1178991-20220829223909590-77792468.jpg

2、动态代理
动态代理是指再运行时,动态生成代理类。

注意:动态代理使用字节码动态生成加载技术,在运行时生成并加载类。

常见的有jdk动态代理,cglib和javassist三种方式。

JDK方式


/**
 * JDK的动态代理
 */
public class JdkDBQueryHandler implements InvocationHandler {

    IDBQuery real=null;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(real==null){
            real=new DBQuery();
        }
        return real.request();
    }

    public static IDBQuery createJdkProxy(){
        IDBQuery jdkProxy=(IDBQuery)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},
                new JdkDBQueryHandler());
        return jdkProxy;
    }
}

CGLIB:


/**
 * cglib方式
 */
public class CglibDBQueryInterceptor implements MethodInterceptor {

    IDBQuery real=null;

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(real==null){
            real=new DBQuery();
        }
        return real.request();
    }

    public static IDBQuery createCglibProxy(){
        Enhancer enhancer=new Enhancer();
        enhancer.setCallback(new CglibDBQueryInterceptor());    //指定切入器
        enhancer.setInterfaces(new Class[]{IDBQuery.class});
        IDBQuery cglibQuery=(IDBQuery)enhancer.create();
        return cglibQuery;
    }
}

结论:jdk方式创建对象很快,但是调用方法较慢。

Hibernate中代理模式的应用:

User u=(User)HibernateSessionFactory.getSession().load(User.class,1);
System.out.print(u.getClass().getName());
System.out.print(u.getName());

以上代码中,load方法后,并没有查询数据库,在调用u.getName()时才查询的数据库,这就是Hibernate用代理模式做了延迟加载。

3、享元模式

概念:如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必每一次使用都创建新的对象。
功能组件如图:

1178991-20220829224012558-1622381163.jpg

注意:享元模式时为数不多的只为了提升系统性能而生的设计模式。他的主要作用就是复用大对象,节省内存和对象创建时间。

享元模式和对象池的最大不同在于:享元模式是不能互相替代的,他们有

/**
 * 报表接口
 */
public interface IReportManager {
    String createReport();
}
/**
 * 员工报表
 */
public class EmployeeReportManager implements IReportManager {

    //租户ID
    protected String   tenanId=null;

    public EmployeeReportManager(String tenanId) {
        this.tenanId = tenanId;
    }

    @Override
    public String createReport() {
        return "this is a employee Report";
    }
}
/**
 * 财务报表
 */
public class FinancialReportManager implements IReportManager {

    //租户ID
    protected String   tenanId=null;

    public FinancialReportManager(String tenanId) {
        this.tenanId = tenanId;
    }

    @Override
    public String createReport() {
        return "this is a Financial Report";
    }
}


/**
 * 享元工厂类
 * 保证同一个id获取到的是同一个对象
 */
public class ReportManagerFactory {
    Map<String,IReportManager> financialReportManager=new HashMap<>();
    Map<String,IReportManager> employeeReportManager=new HashMap<>();
    IReportManager getFinancialReportManager(String tenantId){
        IReportManager r=financialReportManager.get(tenantId);
        if(r==null){
            r=new FinancialReportManager(tenantId);
            financialReportManager.put(tenantId,r);
        }
        return r;
    }

    IReportManager getEmployeeReportManager(String tenantId){
        IReportManager r=employeeReportManager.get(tenantId);
        if(r==null){
            r=new EmployeeReportManager(tenantId);
            employeeReportManager.put(tenantId,r);
        }
        return r;
    }
}
4、装饰者模式

装饰者(Decorator)和被装饰者(ConcreteComponent)拥有相同的接口,装饰者可以在被装饰者的方法上加上特定的前后置处理,增强被装饰者的功能。

/**
 * 接口
 */
public interface IPacketCreator {
    String handleContent();
}

public abstract class PacketDecorator implements IPacketCreator {

    IPacketCreator iPacketCreator;

    public PacketDecorator(IPacketCreator iPacketCreator) {
        this.iPacketCreator = iPacketCreator;
    }
}

public class PacketHTMLHeaderCreator extends PacketDecorator {

    public PacketHTMLHeaderCreator(IPacketCreator iPacketCreator) {
        super(iPacketCreator);
    }

    /**
     * 将数据封装成html格式
     * @return
     */
    @Override
    public String handleContent() {
        StringBuffer sb=new StringBuffer();
        sb.append("<html>");
        sb.append("<body>");
        sb.append(iPacketCreator.handleContent());
        sb.append("</body>");
        sb.append("</html>\n");
        return sb.toString();
    }
}
public class PacketHTTPHeaderCreator extends PacketDecorator {

    public PacketHTTPHeaderCreator(IPacketCreator iPacketCreator) {
        super(iPacketCreator);
    }

    @Override
    public String handleContent() {
        StringBuffer sb=new StringBuffer();
        sb.append("Cache-Control:no-cache\n");
        sb.append(iPacketCreator.handleContent());
        return sb.toString();
    }
}
@Test
    public void test3(){
        IPacketCreator iPacketCreator = new PacketHTTPHeaderCreator(new PacketHTMLHeaderCreator(new PacketBodyCreator()));
        System.out.println(iPacketCreator.handleContent());
    }
5、观察者模式

在软件系统中,当一个对象的行为依赖于另一个对象的状态时,观察者模式就非常有用。

1178991-20220829224059516-216909029.jpg

代码示例如下:

package com.mmc.concurrentcystudy.design.guanchazhe;

import java.util.Observable;
import java.util.Observer;

/**
 * 读者类,实现了观察者接口
 */
public class Reader implements Observer {

    private String name;

    public Reader(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 关注作者
     * @param writerName
     */
    public void subscribe(String writerName){
        WriterManager.getInstance().getWriter(writerName).addObserver(this);
    }


    /**
     * 取消关注
     * @param writerName
     */
    public void unsunbscribe(String writerName){
        WriterManager.getInstance().getWriter(writerName).deleteObserver(this);
    }

    /**
     * 业务方法
     * @param o
     * @param arg
     */
    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof Writer){
            Writer writer=(Writer)o;
            System.out.println(name+"知道"+writer.getName()+"发布了新书《"+writer.getLastNovel()+"》");
        }
    }
}

package com.mmc.concurrentcystudy.design.guanchazhe;

import java.util.Observable;

/**
 * 作者类,要继承自被观察者类
 */
public class Writer extends Observable {

    private String name;      //作者的名称

    private String lastNovel;    //记录作者最新发布的小说

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastNovel() {
        return lastNovel;
    }

    public void setLastNovel(String lastNovel) {
        this.lastNovel = lastNovel;
    }


    public Writer(String name) {
        this.name = name;
        WriterManager.getInstance().add(this);
    }

    //发布新书
    public void addNovel(String novel){
        System.out.println(name+"发布了新书:《"+novel+"》");
        lastNovel=novel;
        setChanged();
        notifyObservers();
    }
}

package com.mmc.concurrentcystudy.design.guanchazhe;

import java.util.HashMap;
import java.util.Map;

/**
 * 管理器,保持一份独有的作者列表
 */
public class WriterManager {

    private Map<String,Writer> map=new HashMap<>();
    public void add(Writer writer){
        map.put(writer.getName(),writer);
    }

    public Writer getWriter(String name){
        return map.get(name);
    }

    //单例
    private WriterManager(){}

    public static WriterManager getInstance(){
        return WriterManagerInstance.writerManager;
    }

    private static  class WriterManagerInstance{
        private static WriterManager writerManager=new WriterManager();
    }
}

测试方法:

@Test
    public void test4(){
        Reader reader=new Reader("小明");
        Reader reader2=new Reader("小红");
        Reader reader3=new Reader("小李");

        Writer writer=new Writer("韩寒");
        Writer writer2=new Writer("李敖");

        reader.subscribe("韩寒");
        reader2.subscribe("韩寒");
        reader3.subscribe("李敖");

        writer.addNovel("三重门");
        writer2.addNovel("不知道");
    }

三、常见的优化组件

1178991-20220829224130788-1289425090.jpg

IO操作很容易形成性能瓶颈,所以尽可能加入缓冲组件。

缓存是为了系统性能而开辟的内存空间。最为简单的缓存是使用HashMap,但是这样做会遇到很多问题,比如不知道合适清理无效的数据,如何防止数据过多而内存溢出。

现在有很多缓存框架,如EHCache,OSCache,JBossCache等。

3、对象复用-“池”

如果一个类被频繁的使用,那么不必每次都生成一个实例,可以将这个实例保存在一个池中,待需要的时候直接从池中获取。这个池就称为对象池。

Apache中提供了一个Jakarta Commons Pool对象池组件,可以直接使用。

API列表:

public interface ObjectPool<T> extends Closeable {
    //从对象池中获取到一个对象
    T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;

    //对象返回给对象池
    void returnObject(T var1) throws Exception;
    }

Common Pool中内置了3个对象池,分别是StackObjectPool,GenericObjectPool,SoftReferenceObjectPool。

  • StackObjectPool:利用Stack来保存对象,可以指定初始化大小。

  • GenericObjectPool:是一个通用的对象池,可以设定对象池的容量,也可以设定无可用对象时应该怎样,有一个复杂的构造函数来定义这些行为。

    1178991-20220829224150851-1913944492.jpg
  • SoftReferenceObjectPool:使用的是ArrayList保存,保存的是对象的软引用。

使用示例:

/**
 * 对象池
 */
public class PoolFactory extends BasePooledObjectFactory<Object> {

    static GenericObjectPool<Object> pool = null;

    // 取得对象池工厂实例
    public synchronized static GenericObjectPool<Object> getInstance() {
        if (pool == null) {
            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            poolConfig.setMaxIdle(-1);
            poolConfig.setMaxTotal(-1);
            poolConfig.setMinIdle(100);
            poolConfig.setLifo(false);
            pool = new GenericObjectPool<Object>(new PoolFactory(), poolConfig);
        }
        return pool;
    }

    public static Object borrowObject() throws Exception{
        return (Object) PoolFactory.getInstance().borrowObject();
    }

    public static void returnObject(Object jdbcUtils) throws Exception{
        PoolFactory.getInstance().returnObject(jdbcUtils);
    }

    public static void close() throws Exception{
        PoolFactory.getInstance().close();
    }

    public static void clear() throws Exception{
        PoolFactory.getInstance().clear();
    }

    @Override
    public Object create() throws Exception {
        return new Object();
    }

    @Override
    public PooledObject<Object> wrap(Object obj) {
        return new DefaultPooledObject<Object>(obj);
    }
} 

测试代码:

@Test
    public void test6() throws Exception {
        Object o=PoolFactory.borrowObject();
        PoolFactory.returnObject(o);
        Object o2=PoolFactory.borrowObject();
        PoolFactory.returnObject(o2);
        System.out.println(o==o2);
    }

注意:只有对重量级对象使用对象池技术才能提高系统性能,对轻量级的对象使用反而会降低性能。

4、并行替代串行
5、负载均衡

为保证应用程序的服务质量,需要使用多台计算机协同工作,将系统负载尽可能分配到各个计算机节点上。

6、时间换空间

一般用于嵌入式设备或者内存,硬盘不足的情况下。
比如一个简单的例子,a和b两个变量的值的替换。最常用的方法是引入一个中间变量。为了省去中间变量可以用这样的方法:

 @Test
    public void test7(){
        int a=3;
        int b=5;
        a=a+b;
        b=a-b;
        a=a-b;
        System.out.println(a);
        System.out.println(b);
    }
7、空间换时间

最典型的应用就是缓存了,除了缓以外,有一些排序方法也会用到。

一个空间换时间的排序示例:

/**
 * 空间换时间的排序
 */
public class SpaceSort {


    public static int arrayLen=1000000;

    public static void main(String[] args) {
        int [] a=new int[arrayLen];
        int [] old=new int[arrayLen];
        Map<Integer,Object> map=new HashMap<>();
        int count=0;
        while (count<a.length){      //初始化数组
            int value=(int)(Math.random()*arrayLen*10);
            if(map.get(value)==null){
                map.put(value,value);
                a[count]=value;
                count++;
            }
        }

        System.arraycopy(a,0,old,0,a.length);
        long start=System.currentTimeMillis();
        Arrays.sort(a);
        System.out.println("Arrays.sort spend:"+(System.currentTimeMillis()-start));

        start=System.currentTimeMillis();
        spaceToTime(old);
        System.out.println("spaceToTime spend:"+(System.currentTimeMillis()-start));

    }

    public static void spaceToTime(int[] array){
        int i=0;
        int max=array[0];
        int l=array.length;
        //找出最大值
        for (i=0;i<l;i++){
            if(array[i]>max)
                max=array[i];
        }

        int []temp=new int[max+1];   //分配临时空间
        for (i=0;i<l;i++){
            temp[array[i]]=array[i];    //以索引下标标识数字大小
        }

        int j=0;
        int max1=max+1;
        for (i=0;i<max1;i++){   //线性复杂度
            if (temp[i]>0){
                array[j++]=temp[i];
            }
        }
    }
}

四、Java程序优化

1、字符串优化

使用StringTokenizer类分割字符串

 public void test9(){
        StringBuilder sb=new StringBuilder();
        int len=1000;
        for (int i=0;i<len;i++){
            sb.append(i);
            sb.append(";");
        }
        String str=sb.toString();
        long start=System.currentTimeMillis();
        StringTokenizer st=new StringTokenizer(str,";");
        for (int i=0;i<10000;i++){
           while (st.hasMoreElements()){
               String s = st.nextToken();
           }
           st=new StringTokenizer(str,";");
        }
        System.out.println(System.currentTimeMillis()-start);

    }

目前来说这种方法也没快多少

2、数据结构

ArrayList和LinkedList

  • 添加元素到队列尾部 ArrayList块
  • 添加到任意位置 LinkedList快

删除对比:

1178991-20220829224216253-1144434439.jpg
  • 在能够有效评估ArrayList数组大小时,指定容量大小能对性能有提升
  • LinkedList不要用for(int i=0;i<list.size();i++)遍历,用foreach。即如果需要通过索引下标对集合进行访问,那最好用ArrayList

TreeMap和LinkedHashMap的区别。
他们都是可排序的,LinkedHashMap基于元素进入集合或者被访问的先后顺序排序,TreeMap是根据元素的固有顺序(Comparator或者Comparable)

3、优化集合访问方法
  1. 分离循环中被重复调用的代码
    如下面的那个list.size()会多次调用。但实际上这个方法也很快
   for (int i=0;i<list.size();i++){
            count++;
        }
        len=list.size();
        for (int i=0;i<len;i++){
            count++;
        }
  1. 减少方法调用
    如果可以直接访问内部元素,就不用调用对应的接口。因为函数调用是需要消耗系统资源的。
4、NIO提升性能

在读写文件上使用NIO会更快
以写入4000000的int数字为例

--- Stream ByteBuffer MappedByteBuffer
写耗时 295ms 123ms 32ms
读耗时 433ms 59ms 17ms

测试示例:

/**
     * IO写文件
     * @throws IOException
     */
    @Test
    public void test12() throws IOException {
        long start=System.currentTimeMillis();
        DataOutputStream dos=new DataOutputStream(new BufferedOutputStream(new FileOutputStream("d://1.txt")));
        for (int i=0;i<4000000;i++){
            dos.writeInt(i);
        }
        if(dos!=null)
            dos.close();
        System.out.println(System.currentTimeMillis()-start);
    }

    /**
     * IO读文件
     * @throws IOException
     */
    @Test
    public void test13() throws IOException {
        long start=System.currentTimeMillis();
       DataInputStream dis=new DataInputStream(new BufferedInputStream(new FileInputStream("d://1.txt")));
       for (int i=0;i<4000000;i++){
           dis.readInt();
       }
       if(dis!=null)
           dis.close();
        System.out.println(System.currentTimeMillis()-start);
    }

    /**
     * NIO写文件
     * @throws IOException
     */
    @Test
    public void test14() throws IOException {
        long start=System.currentTimeMillis();
        FileOutputStream fos=new FileOutputStream("d:/1.txt");
        FileChannel fc=fos.getChannel();
        ByteBuffer byteBuffer=ByteBuffer.allocate(4000000*4);
        for (int i = 0; i <4000000 ; i++) {
            byteBuffer.put(int2byte(i));
        }
        byteBuffer.flip();
        fc.write(byteBuffer);
        System.out.println(System.currentTimeMillis()-start);
    }

    /**
     * NIO读文件
     * @throws IOException
     */
    @Test
    public void test15() throws IOException {
        long start=System.currentTimeMillis();
        FileInputStream fin=new FileInputStream("d://1.txt");
        FileChannel channel = fin.getChannel();
        ByteBuffer byteBuffer=ByteBuffer.allocate(4000000*4);
        channel.read(byteBuffer);
        channel.close();
        byteBuffer.flip();
        while (byteBuffer.hasRemaining()){
            byte2int(byteBuffer.get(),byteBuffer.get(),byteBuffer.get(),byteBuffer.get());
        }
        System.out.println(System.currentTimeMillis()-start);
    }

    public static byte[] int2byte(int res){
        byte[] targets=new byte[4];
        targets[3]=(byte)(res&0xff);
        targets[2]=(byte)((res>>8)&0xff);
        targets[1]=(byte)((res>>16)&0xff);
        targets[0]=(byte)(res>>24);
        return targets;
    }

    public static int byte2int(byte b1,byte b2,byte b3,byte b4){
        return ((b1&0xff)<<24)|((b2&0xff)<<16)|((b3&0xff)<<8)|(b4&0xff);
    }
    
     /**
     * NIO MappedByteBuffer写文件
     * @throws IOException
     */
    @Test
    public void test16() throws IOException {
        long start=System.currentTimeMillis();
       FileChannel fc=new RandomAccessFile("d://1.txt","rw").getChannel();
        IntBuffer ib=fc.map(FileChannel.MapMode.READ_WRITE,0,4000000*4).asIntBuffer();
        for (int i = 0; i < 4000000; i++) {
            ib.put(i);
        }
        if(fc!=null)
            fc.close();
        System.out.println(System.currentTimeMillis()-start);
    }

    /**
     * NIO MappedByteBuffer读文件
     * @throws IOException
     */
    @Test
    public void test17() throws IOException {
        long start=System.currentTimeMillis();
      FileChannel fc=new FileInputStream("d://1.txt").getChannel();
      IntBuffer ib=fc.map(FileChannel.MapMode.READ_ONLY,0,fc.size()).asIntBuffer();
      while (ib.hasRemaining()){
          ib.get();
      }
    if(fc!=null)
        fc.close();
        System.out.println(System.currentTimeMillis()-start);
    }

2、直接内存访问
DirectBuffer:直接可以访问系统物理内存的类,在对普通的ByteBuffer访问时,系统会使用一个“内核缓冲区”进行间接的操作,而DirectBuffer所处的位置就相当于这个“内核缓冲区”。因此他更接近底层,更快。

但是DirectBuffer创建销毁都比较费时间,在需要频繁创建和销毁的情况下不适合用。

/**
     * 使用DirectBuffer
     * @throws IOException
     */
    @Test
    public void test18() throws IOException {
        long start=System.currentTimeMillis();
       ByteBuffer b=ByteBuffer.allocateDirect(500);
       for (int i=0;i<100000;i++){
           for (int j=0;j<99;j++)
               b.putInt(j);
          b.flip();
           for (int j=0;j<99;j++)
               b.getInt();
           b.clear();
       }
        System.out.println(System.currentTimeMillis()-start);
    }

    @Test
    public void test19() throws IOException {
        long start=System.currentTimeMillis();
        ByteBuffer b=ByteBuffer.allocate(500);
        for (int i=0;i<100000;i++){
            for (int j=0;j<99;j++)
                b.putInt(j);
            b.flip();
            for (int j=0;j<99;j++)
                b.getInt();
            b.clear();
        }
        System.out.println(System.currentTimeMillis()-start);
    }
5、引用类型
  1. 软引用
    GC的时候,因为内存没满,没有被回收。
@Test
    public void test20(){
        MyObject myObject=new MyObject();
        //创建引用队列
        ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
        //创建软引用
        SoftReference<MyObject> softReference=new SoftReference<>(myObject,referenceQueue);
        myObject=null;
        System.gc();
        System.out.println("After Gc:soft get="+softReference.get());
        System.out.println("分配大内存");
        byte[] b=new byte[4*1024*925];
        System.out.println("After new byte[]:soft get="+softReference.get());
    }
  1. 弱引用
    在GC的时候一旦发现有弱引用,直接被回收
@Test
    public void test20(){
        MyObject myObject=new MyObject();
        //创建引用队列
        ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
        //创建软引用
        WeakReference<MyObject> softReference=new WeakReference<>(myObject,referenceQueue);
        myObject=null;
        System.gc();
        System.out.println("After Gc:soft get="+softReference.get());
        System.out.println("分配大内存");
        byte[] b=new byte[4*1024*925];
        System.out.println("After new byte[]:soft get="+softReference.get());
    }
  1. 虚引用
    虚引用是引用类型最弱的一个,他的作用在于跟踪垃圾回收。
  2. WeakHashMap
    当需要使用HashMap做一个简单的缓存时,建议使用WeakHashMap,他是弱引用的,可以在内存满的情况下,GC时清除没有被引用的表项
6、改善性能小技巧
  1. 使用局部变量

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

 @Test
    public void test21() throws IOException {
        long start=System.currentTimeMillis();
        int a=0;
        for (int i=0;i<2000000000;i++){
                a++;
        }
        System.out.println(a);
        System.out.println(System.currentTimeMillis()-start);
    }

    private static int ta=0;

    @Test
    public void test22() throws IOException {
        long start=System.currentTimeMillis();
        int a=0;
        for (int i=0;i<2000000000;i++){
            ta++;
        }
        System.out.println(ta);
        System.out.println(System.currentTimeMillis()-start);
    }
  1. 位运算代替乘除法

a*2 用a<<1

a/2 用a>>1
3. 替代switch

用数组替代switch,效率会更高

 public int sw(int a){
        switch (a){
            case 1:return 1;
            case 2:return 3;
            case 3:return 5;
            case 4:return 9;
            default:return 0;
        }
    }
    
    //用数组来实现
    public int sw2(int a){
        int[] array=new int[]{1,3,5,9,0};
        return array[a];
    }

4、复制数组用System.arraycopy
System.arraycopy时浅拷贝。对于非基本类型而言,他拷贝的是对象的引用,而非新建一个对象。

5、clone方法代替new

clone方法不会调用构造函数,所以能够快速的创造一个实例。默认情况下是浅拷贝。但是拷贝的对象修改属性,旧的对象的值可能是不会变的。

6、静态方法代替实例方法

实例方法需要维护一张类似虚拟结构表的东西以支持对多态的实现。所以比静态方法慢。

__EOF__


Recommend

  • 19
    • kimmking.blog.csdn.net 3 years ago
    • Cache

    JVM 问题排查分析上篇(调优经验)

    JVM 问题排查分析上篇(调优经验)

  • 4

    Elasticsearch学习系列二(基础操作) 本文将分为3块讲...

  • 3

    Query文档搜索机制剖析 1. query then fetch(默认搜索方式) 搜索步骤如下: 发送查询到每个shard 找到所有匹配的文档,并使用本地的Term/Document Frequery信息进行打分

  • 4
    • www.cnblogs.com 2 years ago
    • Cache

    MongoDB基础入门 - 女友在高考

    MongoDB简介 MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据...

  • 2

    MongoDB慢查询 慢查询分析 开启内置的慢查询分析器 db.setProfilingLevel(n,m),n的取值可选0,1,2 0:表示不记录 1:表示记录慢速操作,如果值为1...

  • 7
    • www.cnblogs.com 2 years ago
    • Cache

    Hadoop集群搭建 - 女友在高考

    Apache Hadoop 分布式集群搭建 基础环境准备 三台 linux 节点,操作系统(Centos7) 关闭防火墙 systemctl stop firewalld...

  • 1
    • www.cnblogs.com 2 years ago
    • Cache

    HDFS基础入门 - 女友在高考

    HDFS简介 HDFS(全称:Hadoop Distribute File System)分布式文件系统,是Hadoop核心组成。 HDFS中的重要概念 分块存储 HDFS中的文件在物理上是分块存储的,块的大小可以通...

  • 3

    MapReduce 中的排序 MapTask 和 ReduceTask 都会对数据按key进行排序。该操作是 Hadoop 的默认行为,任何应用程序不管需不需要都会被排序。默认排序是字典顺序排序,排序方法是快速排序 下面介绍排序过程...

  • 3
    • www.cnblogs.com 2 years ago
    • Cache

    HBase概念入门 - 女友在高考

    HBase概念入门 - 女友在高考 - 博客园 HBase简介 HBase基于Google的BigTable论文而来,是一个分布式海量列式非关系型数据库系...

  • 7
    • www.cnblogs.com 2 years ago
    • Cache

    Flink基础概念入门 - 女友在高考

    Flink 概述 什么是 Flink     Apache Apache Flink 是一个开源的流处理框架,应用于分布式、高性能、高可用的数据流应用程序。可以处理有限数据流和无限数据,即能够处理有边界和无边界的数据流。无边界的数据...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK