• 性能调优读书笔记(上篇)


    一、Amdahl定律

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

    二、设计模式

    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、代理模式用于延迟加载

    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、享元模式

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

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

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

    /**
     * 报表接口
     */
    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("");
            sb.append("");
            sb.append(iPacketCreator.handleContent());
            sb.append("");
            sb.append("\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、观察者模式

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

    代码示例如下:

    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 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("不知道");
        }
    

    三、常见的优化组件

    1、缓冲

    示意图:

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

    2、缓存

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

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

    3、对象复用-“池”

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

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

    API列表:

    public interface ObjectPool extends Closeable {
        //从对象池中获取到一个对象
        T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
    
        //对象返回给对象池
        void returnObject(T var1) throws Exception;
        }
    

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

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

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

    • SoftReferenceObjectPool:使用的是ArrayList保存,保存的是对象的软引用。

    使用示例:

    /**
     * 对象池
     */
    public class PoolFactory extends BasePooledObjectFactory {
    
        static GenericObjectPool pool = null;
    
        // 取得对象池工厂实例
        public synchronized static GenericObjectPool getInstance() {
            if (pool == null) {
                GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
                poolConfig.setMaxIdle(-1);
                poolConfig.setMaxTotal(-1);
                poolConfig.setMinIdle(100);
                poolConfig.setLifo(false);
                pool = new GenericObjectPool(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 wrap(Object obj) {
            return new DefaultPooledObject(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 map=new HashMap<>();
            int count=0;
            while (count//初始化数组
                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;iif(array[i]>max)
                    max=array[i];
            }
    
            int []temp=new int[max+1];   //分配临时空间
            for (i=0;iarray[i]]=array[i];    //以索引下标标识数字大小
            }
    
            int j=0;
            int max1=max+1;
            for (i=0;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";");
            }
            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快

    删除对比:

    • 在能够有效评估ArrayList数组大小时,指定容量大小能对性能有提升
    • LinkedList不要用for(int i=0;i

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

    3、优化集合访问方法
    1. 分离循环中被重复调用的代码
      如下面的那个list.size()会多次调用。但实际上这个方法也很快
       for (int i=0;ilen=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. 强引用
    2. 软引用
      GC的时候,因为内存没满,没有被回收。
    @Test
        public void test20(){
            MyObject myObject=new MyObject();
            //创建引用队列
            ReferenceQueue referenceQueue = new ReferenceQueue<>();
            //创建软引用
            SoftReference 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 referenceQueue = new ReferenceQueue<>();
            //创建软引用
            WeakReference 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__

  • 本文作者: 女友在高考
  • 本文链接: https://www.cnblogs.com/javammc/p/16637682.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    SpringCloud Gateway如何设置keep-alive
    Unexpected mutation of “dialogable“ prop.eslintvue/no-mutating-props
    【libevent】异步UDP
    一个基于 gin+ grpc + etcd 等框架开发的小栗子
    Pod环境变量和initContainer
    人大金仓助力国家电网调度中心培养国产数据库专家人才
    数据分析之数据预处理、分许建模、可视化
    雅思口语 23九月换题季最新考题答案
    【黄色手套22】6话:构造数据类型
    4 搜索插入位置
  • 原文地址:https://www.cnblogs.com/javammc/p/16637682.html