zoukankan      html  css  js  c++  java
  • java 笔记

    Java nio

    http://www.cnblogs.com/javaee6/p/4935154.html

    基础知识

    1. Java 虚拟机使用了即时编译器,因此采用java编写的热点代码其运行速度与C++相当。
    1. Java与平台无关,在java中int4字节,而C和C++整型依赖于具体的操作系统,对于DOS整型数值占2字节,而windows系统32的整型数值占4字节。

    3.&和| 运算符和&&||不同,第一种两边都算,第二种是短路的

    4.>>>运算符将用0填充前面的位,>>运算符用符号位填充左侧的位,当需要构建位模式屏蔽某些位时,可以用位运算提高运行效率。

    5.代码点:

    6.Scanner in = new Sanner(System.in);

    读取一行:in.nextLine();

    读取一个字符:in.next();

    读取一个数字:in.nextInt();

    7.Break 跳出循环 可以在标签 不是循环也可以 continue 也可以带标签

    8.Biginteger 和 BigDecimal是java处理大数据自带的两个类型,有相应的加减乘除的函数

    9.匿名数组用处在不创建新变量的情况下重新初始化一个数组

    10.在编写一个结果为数组的方法时如果碰巧结果为空,就可以创建一个长度为0的数组

    11.抽取一个数组里的数,确保不会再次抽取到那个数值,使用数组中的最后一个数值改写number[r],并将n减1。   Number[r] = Number[n-1]; n--; 抽取的数已经没用。

    12.Balances[][] 二维数组 , for(double[] row : balances)

                              for(double b : row)

                                 do something with b

    13.类之间的关系主要有  依赖,聚合,继承  最好用聚合降低类之间的耦合度

    14.定义一个 public static final PI = 3.141596265;好处:使用了static可以减少内存开销每个

    15.包含PI的类只用了一个共享的内存空间,使用了final防止修改。

    16.没有main 的hello world

     Public class hello{static {System.out.println(“hello, world”);} System.exit(0);}

    17.静态导入:import可以导入静态方法和静态域,import java.math; sqrt(pow(x,2));就不用Math.pow()很方便

    18.编译器在编译源文件的时候不检查目录结构,例如,假定有一个源文件开头有下列语句:package com.mycompany;即使这个源文件没有在子目录com/mycompany下,也可以进行编译。如果他不依赖其他包,就不会出现编译错误,但是最终的程序将无法运行,这是因为虚拟机找不到类文件。

    19.一旦整理了数组列表的大小,添加新元素就需要花时间再次移动存储块,所以应该在确认不会添加任何元素时,在调用trimToSize方法,将存储区域的大小调整为当前元素数量所属要的存储空间数目。垃圾回收器将回收多余的存储空间

    20.内部类和匿名类可以节省一些录入的时间,隐藏类似包的功能,但代码会变得混乱

    21.代理类可以在运行时创建全新的类。

    22.inputStream和outputStream 类仅仅读取和写入单个的字节和字节数组,他们没有读取和写入字符串和数值的方法。对于unicode文本来说使用Reader和writer类

    23.不要调用Thread类或Runnable对象的run方法,直接调用run方法只会在当前线程中执行任务,并不会启动新的线程,正确的做法是调用Thread.start方法,它会创建一个新的线程来执行run方法。

    24.使用synchronized好处,监控虚拟机的工具可以提交关于隐式的锁和条件状态的报告,这对于调试死锁问题很有帮助。

    25.volatile关键字为对一个实例的同步访问提供了一个免锁机制,如果你把域声明为volatile那么编译器和虚拟机就知道该域可能会被另一个线程并发更新。

    26.BlockingQueue 子类)ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue,SynchronousQueue

    27.多线程http://blog.csdn.net/johnny901114/article/details/8695708

    28.Vector类的所有方法都是同步的,两个线程安全地访问vector对象。

    29.Collections类中静态的synchronizedMap方法可以将任何一个映射表转换成一个具有同步访问方法的Map.  Map = Collections.synchronizedMap(map);

    30.hashtable 与 vector一样 各个方法都是同步的

    31.list set都是单例 list有序列表方式存的慢  set以数组存的查找快无序 LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。

    MVC框架显示层——Velocity技术

    http://blog.csdn.net/fasttalk/article/details/398059

    反射与注解的使用

    作用:注解是附加在代码中的一些信息,用于编译、运行时解析和使用,起到说明配置作用 

    主要学习两方面1 元注解,2 自定义注解 

    一、元注解 

    包括:1.@Target,  2.@Retention  3.@Documented, 4.@Inherited 几种

    1 @Target 

      作用:用于描述注解的使用范围(即 注解是描述:包、类、字段、方法、参数、接口等)

      取值:1.CONSTRUCTOR:用于描述构造器

        2.FIELD:用于描述域

        3.LOCAL_VARIABLE:用于描述局部变量

        4.METHOD:用于描述方法

        5.PACKAGE:用于描述包

        6.PARAMETER:用于描述参数

        7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

     

    @Target(ElementType.TYPE) //注解用于描述类的

    public @interface Table {

        /**

         * 数据表名称注解,默认值为类名称

         * @return

         */

        public String tableName() default "className";

    }

     

    @Target(ElementType.FIELD) //注解用在字段上

    public @interface NoDBColumn {

    }

     

    2.@Retention

      作用:描述注解的生命周期(1 仅存在源码中,不会被编译 2 存在编译器中,不会在运行中取到 3 存在与类运行期,运行时可以通过反射读取)

      取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)

        2.CLASS:在class文件中有效(即class保留)

        3.RUNTIME:在运行时有效(即运行时保留)

     

    @Target(ElementType.FIELD)

    @Retention(RetentionPolicy.RUNTIME)//注解可以在运行期通过反射取得

    public @interface Column {

        public String name() default "fieldName";

    }

     

    3.@Documented,

     @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

     

    4.@Inherited

    @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类

     

     

    二、自定义注解

          使用@interface自定义注解,自动继承了java.lang.annotation.Annotation接口,其中每一个方法声明一个配置参数,方法名称就是参数名称,

      返回值就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

     

    例子:两个注解,分别标识实体对应的表和数据库中字段

    1 类注解 

    Java代码  

    1. @Target(value=ElementType.TYPE)//表示该注解用于类上面  
    2. @Retention(RetentionPolicy.RUNTIME) //运行时可以通过反射取得  
    3. public @interface Table {  
    4.     public String name() default "";  
    5. }  

     

       2 属性注解,并且注解有两个属性

    Java代码  

    1. <span style="color: #ff6600;">@Target(value=ElementType.FIELD)  
    2. @Retention(RetentionPolicy.RUNTIME)</span>  
    3. public @interface Column {  
    4.     public String value() default "";   
    5.     public boolean isKey() default false;  
    6. }  

     

      3 实体 

      

    Java代码  

    1. package anotation;  
    2.   
    3. @Table(name="s_user")  
    4. public class User {  
    5.       
    6.     @Column(value="id",isKey=true)  
    7.     private Integer id;  
    8.       
    9.     @Column("s_name") /<span style="color: #ff6600;">/没有指定注解的name 则默认name为value 等同于 value="s_name"</span>  
    10.     private String name;  
    11.       
    12.     @Column(value="s_age")  
    13.     private Integer age;  
    14.       
    15.     public Integer getId() {  
    16.         return id;  
    17.     }  
    18.     public void setId(Integer id) {  
    19.         this.id = id;  
    20.     }  
    21.     public String getName() {  
    22.         return name;  
    23.     }  
    24.     public void setName(String name) {  
    25.         this.name = name;  
    26.     }  
    27.     public Integer getAge() {  
    28.         return age;  
    29.     }  
    30.     public void setAge(Integer age) {  
    31.         this.age = age;  
    32.     }  
    33. }  

      4  测试类,通过反射得到类的各种信息 

      

    Java代码  

    1. public class Test {  
    2.       
    3.     public static void main(String[] args) throws  Exception {  
    4.         User user = new User();  
    5.         user.setId(1);  
    6.         user.setName("wang");  
    7.         user.setAge(89);  
    8.           
    9. //      Class<User> userClazz = (Class<User>) Class.forName( "anotation.User");  
    10.         Class  userClazz = user.getClass();  
    11.         Table table = (Table) userClazz.getAnnotation(Table.class); //得到类的注解   
    12.         System.out.println("表名称 : "+table.name() ); //取得注解的名称 属性  
    13.           
    14.         Field[]  fields = userClazz.getDeclaredFields(); //得到所有的 对象的属性   
    15.         for(int i=0;i<fields.length;i++ ){  
    16.             Field field = fields[i];  
    17.               
    18.             Column column  = field.getAnnotation(Column.class );//得到属性上的注解   
    19.             System.out.println( "column的注解名称:"+column.value()+"   注解:isKey===  "+column.isKey() );//打印属性上的注解  
    20.               
    21.             String fieldName = field.getName(); //得到属性名称   
    22.             Class clazz = field.getType();//得到属性类型   
    23.             System.out.println(fieldName + "   字段类型    "+clazz );  
    24.               
    25.             field.setAccessible(true);//可以访问私有变量   
    26.             System.out.println( fieldName + "  调用字段变量取得字段值  "+ field.get(user ) );//打印对象的该属性的值   
    27.             Method method = userClazz.getMethod("get"+toUpperCaseFirstOne(fieldName) );//调用get取得该对象的值  
    28.             System.out.println( fieldName + "  调用字段get方法取出字段值  "+  method.invoke(user) );  
    29.               
    30.               
    31.             System.out.println("判断字段类型是否为整形:"+( clazz==Integer.class));//判断一个字段的类型    
    32.             System.out.println( "============");  
    33.               
    34.         }  
    35. //      Object name = 1;  
    36. //      System.out.println( name.getClass().equals( Integer.class) );  
    37.           
    38.     }  
    39.       
    40.   
    41.     /** 
    42.      * 首字母转大写 
    43.      * @param s 
    44.      * @return 
    45.      */  
    46.     public static String toUpperCaseFirstOne(String s)  
    47.     {  
    48.         if(Character.isUpperCase(s.charAt(0)))  
    49.             return s;  
    50.         else  
    51.             return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();  
    52.     }  
    53. }  

    单元测试mokitto

    ThreadLocal

    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    c3p0三种配置方式

    c3p0的配置方式分为三种,分别是
    1.setters一个个地设置各个配置项
    2.类路径下提供一个c3p0.properties文件
    3.类路径下提供一个c3p0-config.xml文件

    1.setters一个个地设置各个配置项
    这种方式最繁琐,形式一般是这样:

    01

    Properties props = new Properties();

    02

    InputStream in = ConnectionManager.class.getResourceAsStream("/c3p0.properties");

    03

    props.load(in);

    04

    in.close();

    05

     

    06

    ComboPooledDataSource cpds = new ComboPooledDataSource();

    07

    cpds.setDriverClass(props.getProperty("driverClass"));

    08

    cpds.setJdbcUrl(props.getProperty("jdbcUrl"));

    09

    cpds.setUser(props.getProperty("user"));

    10

    cpds.setPassword(props.getProperty("password"));

                         

    因为繁琐,所以很不适合采用,于是文档提供了另外另种方式。

    2. 类路径下提供一个c3p0.properties文件 
    文件的命名必须是c3p0.properties,里面配置项的格式为:

    1

    c3p0.driverClass=com.mysql.jdbc.Driver

    2

    c3p0.jdbcUrl=jdbc:mysql://localhost:3306/jdbc

     

    3

    c3p0.user=root

    4

    c3p0.password=java

    上面只提供了最基本的配置项,其他配置项参照 文档配置,记得是c3p0.后面加属性名就是了,最后初始化数据源的方式就是这样简单: 

    1

    private static ComboPooledDataSource ds = new ComboPooledDataSource();

    3

    public static Connection getConnection() {

    4

        try {

    5

            return ds.getConnection();

    6

        } catch (SQLException e) {

               

     

    7

            throw new RuntimeException(e);

    8

        }

     

    }

    3.类路径下提供一个c3p0-config.xml文件  
    这种方式使用方式与第二种差不多,但是有更多的优点 
    (1).更直观明显,很类似hibernate和spring的配置 
    (2).可以为多个数据源服务,提供default-config和named-config两种配置方式 
    下面是一个配置模板: 

    01

    <c3p0-config>

    02

      <default-config>  

    03

        <property name="user">root</property>

    04

        <property name="password">java</property>

    05

        <property name="driverClass">com.mysql.jdbc.Driver</property>

    06

        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>

    07

         

    08

        <property name="initialPoolSize">10</property>

    09

        <property name="maxIdleTime">30</property>

    10

        <property name="maxPoolSize">100</property>

    11

        <property name="minPoolSize">10</property>

    12

      </default-config>

    14

      <named-config name="myApp">

     

        <property name="user">root</property>

    16

        <property name="password">java</property>

    17

        <property name="driverClass">com.mysql.jdbc.Driver</property>

    18

        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>

    20

        <property name="initialPoolSize">10</property>

    21

        <property name="maxIdleTime">30</property>

    22

        <property name="maxPoolSize">100</property>

    23

        <property name="minPoolSize">10</property>

    24

      </named-config>

    25

    </c3p0-config>

                                                           

    如果要使用default-config则初始化数据源的方式与第二种一样,如果要使用named-config里面配置初始化数据源,则只要使用一个带参数的ComboPooledDataSource构造器就可以了 

    1

    private static ComboPooledDataSource ds = new ComboPooledDataSource("myApp");

       
       
           

    Comparable和comparator区别

    都是接口,但是comparable是类实现的接口,写在类的里面,comparator是自己单独的比较类,这样不需要修改要实现比较的类,comparable实现comparTo方法一个参数,comparator实现compare方法俩个参数。数组就可以使用Collections.sort();进行排序。

    <? extends T > 与<? super T>区别

    泛型的加入,就是在为把一些在运行期间的报的错误,通过泛型放到编译期间,java是强类型语言虚拟机在运行时是没有泛型存在<? extends T > 与<? super T>是一个范围但是在虚拟机里也是唯一的确定的一个子类或超类,在编译期间会进行类型擦除,所以Static域中不能使用T,而Static域中的其它变量也是不同诸泛型实例共享的。<? extends T >检测的类型是T或者T的子类们,<? super T>检测的类型是T或者T的父类们。List<? extends T > list,list不能用add 方法,因为要加入的是T或T类型的子类,加入时编译器不知道你到底加入的是T的子类还是子类的子类就报错了,而用T a = get(0)时,在类型擦除是编译器用T作为类型,父类引用是可以指向子类的所以是安全的。List<? super T > list,list中可以用add方法,只能addT的子类型,T的父类型编译器也不知道你到底add的是父类还是父类的父类。Get的时候编译器不知道你取得是T的什么父类,T a = get(0)而子类型不可以指向父类型会报错。

    父类能指向子类,子类不能指向父类。所以这两个方法extends会比super更受一些限制,所以他俩方法执行上不是对称的。

    public static void main(String[] args) { 

              person a = new person( "p1", 1);

              person b = new person( "p2", 2);

              bigPerson aa = new bigPerson( "b1", 3, 25);

              bigPerson bb = new bigPerson( "b2", 2, 26);

              smallPerson aaa = new smallPerson( "aa", 1);

              smallPerson bbb = new smallPerson( "bb", 2);

             

              System.out.println(findMax(new smallPerson[]{aaa,bbb}, new personCmp()).name);

          } 

          public static < T extends person> T findMax( T[] arr,Comparator< ? super T> cmp){

             int maxIndex = 0;

             for(int i = 0; i < arr.length; i++){

                 if( cmp.compare( arr[i], arr[maxIndex]) > 0){

                    maxIndex = i;

                 }

             }

             return arr[maxIndex];

          }

          static class   bigpersoncmp implements Comparator<bigPerson>{

           @Override

           public int compare(bigPerson o1, bigPerson o2) {

               // TODO Auto-generated method stub

               if( o1.jjSize > o2.jjSize){

                  return 1;

               }else if( o1.jjSize == o2.jjSize){

                  return 0;

               }else{

                  return -1;

               }

           }

          }

          static class  personCmp implements Comparator<person>{

           @Override

           public int compare(person o1, person o2) {

               // TODO Auto-generated method stub

               if( o1.age > o2.age){

                  return 1;

               }else if( o1.age == o2.age){

                  return 0;

               }else{

                  return -1;

               }

           }

          }

    Java编程性能优化一些事儿  http://my.oschina.net/xianggao/blog/77224

    在JAVA程序中,性能问题的大部分原因并不在于JAVA语言,而是程序本身。养成良好的编码习惯非常重要,能够显著地提升程序性能。

    1. 尽量在合适的场合使用单例

    使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:

    第一,控制资源的使用,通过线程同步来控制资源的并发访问;

    第二,控制实例的产生,以达到节约资源的目的;

    第三,控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信。

    2. 尽量避免随意使用静态变量

    要知道,当某个对象被定义为static变量所引用,那么GC通常是不会回收这个对象所占有的内存,如

    public class A{

       private static B b = new B();

    }

    此时静态变量b的生命周期与A类同步,如果A类不会卸载,那么b对象会常驻内存,直到程序终止。

    3. 尽量避免过多过常的创建Java对象

    尽量避免在经常调用的方法,循环中new对象,由于系统不仅要花费时间来创建对象,而且还要花时间对这些对象进行垃圾回收和处理,在我们可以控制的范围内,最大限度的重用对象,最好能用基本的数据类型或数组来替代对象。

    4. 尽量使用final修饰符

    带有final修饰符的类是不可派生的。在JAVA核心API中,有许多应用final的例子,例如java.lang.String,为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。

    如:让访问实例内变量的getter/setter方法变成”final:

    简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”,例子:

    class MAF {

         public void setSize (int size) {

              _size = size;

         }

         private int _size;

    }

    更正

    class DAF_fixed {

         final public void setSize (int size) {

              _size = size;

         }

         private int _size;

    }

    5. 尽量使用局部变量

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

    6. 尽量处理好包装类型和基本类型两者的使用场所

    虽然包装类型和基本类型在使用过程中是可以相互转换,但它们两者所产生的内存区域是完全不同的,基本类型数据产生和处理都在栈中处理,包装类型是对象,是在堆中产生实例。在集合类对象,有对象方面需要的处理适用包装类型,其他的处理提倡使用基本类型。

    7. 慎用synchronized,尽量减小synchronize的方法

    都知道,实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。synchronize方法被调用时,直接会把当前对象锁 了,在方法执行完之前其他线程无法调用当前对象的其他方法。所以synchronize的方法尽量小,并且应尽量使用方法同步代替代码块同步。

    9. 尽量不要使用finalize方法

    实际上,将资源清理放在finalize方法中完成是非常不好的选择,由于GC的工作量很大,尤其是回收Young代内存时,大都会引起应用程序暂停,所以再选择使用finalize方法进行资源清理,会导致GC负担更大,程序运行效率更差。

    10. 尽量使用基本数据类型代替对象

    String str = "hello";

    上面这种方式会创建一个“hello”字符串,而且JVM的字符缓存池还会缓存这个字符串;

    String str = new String("hello");

    此时程序除创建字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o

    11. 多线程在未发生线程安全前提下应尽量使用HashMap、ArrayList

    HashTable、Vector等使用了同步机制,降低了性能。

    12. 尽量合理的创建HashMap

    当你要创建一个比较大的hashMap时,充分利用这个构造函数1

    public HashMap(int initialCapacity, float loadFactor);

    避免HashMap多次进行了hash重构,扩容是一件很耗费性能的事,在默认中initialCapacity只有16,而loadFactor是 0.75,需要多大的容量,你最好能准确的估计你所需要的最佳大小,同样的Hashtable,Vectors也是一样的道理。

    13. 尽量减少对变量的重复计算

    for(int i=0;i<list.size();i++)

    应该改为

    for(int i=0,len=list.size();i<len;i++)

    并且在循环中应该避免使用复杂的表达式,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。

    14. 尽量避免不必要的创建

    A a = new A();

    if(i==1){list.add(a);}

    应该改为

    if(i==1){

      A a = new A();

      list.add(a);

    }

    15. 尽量在finally块中释放资源

    程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

    16. 尽量使用移位来代替'a/b'的操作

    "/"是一个代价很高的操作,使用移位的操作将会更快和更有效

    int num = a / 4;

    int num = a / 8;

    应该改为

    int num = a >> 2;

    int num = a >> 3;

    但注意的是使用移位应添加注释,因为移位操作不直观,比较难理解

    17.尽量使用移位来代替'a*b'的操作

    同样的,对于'*'操作,使用移位的操作将会更快和更有效

    int num = a * 4;

    int num = a * 8;

    应该改为

    int num = a << 2;

    int num = a << 3;

    18. 尽量确定StringBuffer的容量

    StringBuffer 的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再 丢弃旧的数组。在大多数情况下,你可以在创建 StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。

    StringBuffer buffer = new StringBuffer(1000);

    19. 尽量早释放无用对象的引用

    大部分时,方法局部引用变量所引用的对象 会随着方法结束而变成垃圾,因此,大部分时候程序无需将局部,引用变量显式设为null。

    Public void test(){ 

      Object obj = new Object(); 

      …… 

      Obj=null; 

    }

    上面这个就没必要了,随着方法test()的执行完成,程序中obj引用变量的作用域就结束了。但是如果是改成下面:

    Java代码

    Public void test(){ 

      Object obj = new Object(); 

      …… 

      Obj=null; 

      //执行耗时,耗内存操作;或调用耗时,耗内存的方法

       …… 

    }

    这时候就有必要将obj赋值为null,可以尽早的释放对Object对象的引用。

    20. 尽量避免使用二维数组

    二维数据占用的内存空间比一维数组多得多,大概10倍以上。

    21. 尽量避免使用split

    除非是必须的,否则应该避免使用split,split由于支持正则表达式,所以效率比较低,如果是频繁的几十,几百万的调用将会耗费大量资源,如果确实需要频繁的调用split,可以考虑使用apache的StringUtils.split(string,char),频繁split的可以缓存结果。

    22. ArrayList & LinkedList

    一个是线性表,一个是链表,一句话,随机查询尽量使用ArrayList,ArrayList优于LinkedList,LinkedList还要移动指针,添加删除的操作LinkedList优于ArrayList,ArrayList还要移动数据,不过这是理论性分析,事实未必如此,重要的是理解好2者得数据结构,对症下药。

    23. 尽量使用System.arraycopy ()代替通过来循环复制数组

    System.arraycopy() 要比通过循环来复制数组快的多

    24. 尽量缓存经常使用的对象

    尽可能将经常使用的对象进行缓存,可以使用数组,或HashMap的容器来进行缓存,但这种方式可能导致系统占用过多的缓存,性能下降,推荐可以使用一些第三方的开源工具,如EhCache,Oscache进行缓存,他们基本都实现了FIFO/FLU等缓存算法。

    25. 尽量避免非常大的内存分配

    有时候问题不是由当时的堆状态造成的,而是因为分配失败造成的。分配的内存块都必须是连续的,而随着堆越来越满,找到较大的连续块越来越困难。

    26. 慎用异常

    当创建一个异常时,需要收集一个栈跟踪(stack track),这个栈跟踪用于描述异常是在何处创建的。构建这些栈跟踪时需要为运行时栈做一份快照,正是这一部分开销很大。当需要创建一个 Exception 时,JVM 不得不说:先别动,我想就您现在的样子存一份快照,所以暂时停止入栈和出栈操作。栈跟踪不只包含运行时栈中的一两个元素,而是包含这个栈中的每一个元素。

    如果您创建一个 Exception ,就得付出代价。好在捕获异常开销不大,因此可以使用 try-catch 将核心内容包起来。从技术上讲,您甚至可以随意地抛出异常,而不用花费很大的代价。招致性能损失的并不是 throw 操作——尽管在没有预先创建异常的情况下就抛出异常是有点不寻常。真正要花代价的是创建异常。幸运的是,好的编程习惯已教会我们,不应该不管三七二十一就抛出异常。异常是为异常的情况而设计的,使用时也应该牢记这一原则。

    27. 尽量重用对象

    特别是String对象的使用中,出现字符串连接情况时应使用StringBuffer代替,由于系统不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理。因此生成过多的对象将会给程序的性能带来很大的影响。

    28. 不要重复初始化变量

    默认情况下,调用类的构造函数时,java会把变量初始化成确定的值,所有的对象被设置成null,整数变量设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键字创建一个对象时,构造函数链中的所有构造函数都会被自动调用。

    这里有个注意,给成员变量设置初始值但需要调用其他方法的时候,最好放在一个方法比如initXXX()中,因为直接调用某方法赋值可能会因为类尚未初始化而抛空指针异常,如:public int state = this.getState();

    29. 在java+Oracle的应用系统开发中,java中内嵌的SQL语言应尽量使用大写形式,以减少Oracle解析器的解析负担。

    30. 在java编程过程中,进行数据库连接,I/O流操作,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销。

    31. 过分的创建对象会消耗系统的大量内存,严重时,会导致内存泄漏,因此,保证过期的对象的及时回收具有重要意义。JVM的GC并非十分智能,因此建议在对象使用完毕后,手动设置成null。

    32. 在使用同步机制时,应尽量使用方法同步代替代码块同步。

    33. 不要在循环中使用Try/Catch语句,应把Try/Catch放在循环最外层

    Error是获取系统错误的类,或者说是虚拟机错误的类。不是所有的错误Exception都能获取到的,虚拟机报错Exception就获取不到,必须用Error获取。

    34. 通过StringBuffer的构造函数来设定他的初始化容量,可以明显提升性能

    StringBuffer的默认容量为16,当StringBuffer的容量达到最大容量时,她会将自身容量增加到当前的2倍+2,也就是2*n+2。无论何时,只要StringBuffer到达她的最大容量,她就不得不创建一个新的对象数组,然后复制旧的对象数组,这会浪费很多时间。所以给StringBuffer设置一个合理的初始化容量值,是很有必要的! 

    35. 合理使用java.util.Vector

    Vector与StringBuffer类似,每次扩展容量时,所有现有元素都要赋值到新的存储空间中。Vector的默认存储能力为10个元素,扩容加倍。

    vector.add(index,obj) 这个方法可以将元素obj插入到index位置,但index以及之后的元素依次都要向下移动一个位置(将其索引加 1)。 除非必要,否则对性能不利。同样规则适用于remove(int index)方法,移除此向量中指定位置的元素。将所有后续元素左移(将其索引减 1)。返回此向量中移除的元素。所以删除vector最后一个元素要比删除第1个元素开销低很多。删除所有元素最好用removeAllElements()方法。

    如果要删除vector里的一个元素可以使用 vector.remove(obj);而不必自己检索元素位置,再删除,如int index = indexOf(obj);vector.remove(index);

    38. 不用new关键字创建对象的实例

    用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用她的clone()方法。clone()方法不会调用任何类构造函数。

    下面是Factory模式的一个典型实现:

    public static Credit getNewCredit()

    {

        return new Credit();

    }

    改进后的代码使用clone()方法:

    private static Credit BaseCredit = new Credit();

    public static Credit getNewCredit()

    {

        return (Credit)BaseCredit.clone();

    }

    39. 不要将数组声明为:public static final

    40. HaspMap的遍历:

    Map<String, String[]> paraMap = new HashMap<String, String[]>();

    for( Entry<String, String[]> entry : paraMap.entrySet() )

    {

        String appFieldDefId = entry.getKey();

        String[] values = entry.getValue();

    }

    利用散列值取出相应的Entry做比较得到结果,取得entry的值之后直接取key和value。

    41. array(数组)和ArrayList的使用

    array 数组效率最高,但容量固定,无法动态改变,ArrayList容量可以动态增长,但牺牲了效率。 

    42. 单线程应尽量使用 HashMap, ArrayList,除非必要,否则不推荐使用HashTable,Vector,她们使用了同步机制,而降低了性能。

    43. StringBuffer,StringBuilder的区别在于:java.lang.StringBuffer 线程安全的可变字符序列。一个类似于String的字符串缓冲区,但不能修改。StringBuilder与该类相比,通常应该优先使用StringBuilder类,因为她支持所有相同的操作,但由于她不执行同步,所以速度更快。为了获得更好的性能,在构造StringBuffer或StringBuilder时应尽量指定她的容量。当然如果不超过16个字符时就不用了。 相同情况下,使用StringBuilder比使用StringBuffer仅能获得10%~15%的性能提升,但却要冒多线程不安全的风险。综合考虑还是建议使用StringBuffer。

    44. 尽量使用基本数据类型代替对象。 

    45. 使用具体类比使用接口效率高,但结构弹性降低了,但现代IDE都可以解决这个问题。

    46. 考虑使用静态方法,如果你没有必要去访问对象的外部,那么就使你的方法成为静态方法。她会被更快地调用,因为她不需要一个虚拟函数导向表。这同事也是一个很好的实践,因为她告诉你如何区分方法的性质,调用这个方法不会改变对象的状态。

    47. 应尽可能避免使用内在的GET,SET方法。

    48.避免枚举,浮点数的使用。

    以下举几个实用优化的例子:

    一、避免在循环条件中使用复杂表达式

    在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。例子:

    import java.util.Vector;

    class CEL {

         void method (Vector vector) {

             for (int i = 0; i < vector.size (); i++)   // Violation

                 ; // ...

         }

    }

    更正:

    class CEL_fixed {

         void method (Vector vector) {

             int size = vector.size ()

             for (int i = 0; i < size; i++)

                 ; // ...

         }

    }

    二、为'Vectors' 和 'Hashtables'定义初始大小

    JVM为Vector扩充大小的时候需要重新创建一个更大的数组,将原原先数组中的内容复制过来,最后,原先的数组再被回收。可见Vector容量的扩大是一个颇费时间的事。

    通常,默认的10个元素大小是不够的。你最好能准确的估计你所需要的最佳大小。例子:

    import java.util.Vector;

    public class DIC {

         public void addObjects (Object[] o) {

             // if length > 10, Vector needs to expand

             for (int i = 0; i< o.length;i++) {   

                 v.add(o);    // capacity before it can add more elements.

             }

         }

         public Vector v = new Vector();   // no initialCapacity.

    }

    更正:

    自己设定初始大小。

    public Vector v = new Vector(20); 

    public Hashtable hash = new Hashtable(10);

    三、在finally块中关闭Stream

    程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

    四、使用'System.arraycopy ()'代替通过来循环复制数组,例子:

    public class IRB

    {

         void method () {

             int[] array1 = new int [100];

             for (int i = 0; i < array1.length; i++) {

                 array1 [i] = i;

             }

             int[] array2 = new int [100];

             for (int i = 0; i < array2.length; i++) {

                 array2 [i] = array1 [i]; // Violation

             }

         }

    }

    更正:

    public class IRB

    {

         void method () {

             int[] array1 = new int [100];

             for (int i = 0; i < array1.length; i++) {

                 array1 [i] = i;

             }

             int[] array2 = new int [100];

             System.arraycopy(array1, 0, array2, 0, 100);

         }

    }

    五、让访问实例内变量的getter/setter方法变成”final”

    简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”,例子:

    class MAF {

         public void setSize (int size) {

              _size = size;

         }

         private int _size;

    }

    更正:

    class DAF_fixed {

         final public void setSize (int size) {

              _size = size;

         }

         private int _size;

    }

    六、对于常量字符串,用'String' 代替 'StringBuffer'

    常量字符串并不需要动态改变长度。

    例子:

    public class USC {

         String method () {

             StringBuffer s = new StringBuffer ("Hello");

             String t = s + "World!";

             return t;

         }

    }

    更正:把StringBuffer换成String,如果确定这个String不会再变的话,这将会减少运行开销提高性能。

    七、在字符串相加的时候,使用 ' ' 代替 " ",如果该字符串只有一个字符的话

    public class STR {

         public void method(String s) {

             String string = s + "d"   // violation.

             string = "abc" + "d"       // violation.

         }

    }

    更正:

    将一个字符的字符串替换成' '

    public class STR {

         public void method(String s) {

             String string = s + 'd'

             string = "abc" + 'd'  

         }

    }

    以上仅是Java方面编程时的性能优化,性能优化大部分都是在时间、效率、代码结构层次等方面的权衡,各有利弊,不要把上面内容当成教条,或许有些对我们实际工作适用,有些不适用,还望根据实际工作场景进行取舍吧,活学活用,变通为宜。

    java回收机制finalize

    1:java采用有向图机制,某个对象不可达时GC开始回收这个对象

    2:当GC回收某个对象时调用对象的finalize方法,这个方法里需要自己处理异常,否则finalize方法会直接丢弃

    3:当GC执行时程序会停止,时间长程序会卡住,时间短对象有回收不完,采用了这种的办法,就是按照一定比例的回收一定的数量,称为增量式GC

    4:finalize方法jvm保证最多只执行一次,还有可能不执行,GC回收不了C++等其他语言分配的内存所以一般用finalize方法,想数据库连接文件等资源关闭不要用写在这里

    5:String s s是句柄 句柄存在于栈中,可以不指向对象,脱离对象存在,String s1= new String("abc") 这会在文字常量区创建一个abc 又会在堆中创建abc 所以这句话创建了两个字符串。String s1 = new String("d");String s2 = new String(
    "d"); 这两句总共创建了三个对象,一个常量区,两个在堆中,字符常量区的文本只存一份!

    垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新覆盖这个方法,因为Object已经实现了一个默认的,除非我们要实现特殊的功能(这 里面涉及到很多东西,比如对象空间树等内容)。

    不过用Java以外的代码编写的Class(比如JNI,C++的new方法分配的内存),垃圾回收器并不能对这些部分进行正确的回收,这时就需要我们覆盖默认的方法来实现对这部分内存的正确释放和回收(比如C++需要delete)。

    总之,finalize相当于析构函数,他是垃圾回收器回收一个对象的时候第一个要调用的方法。不过由于Java的垃圾回收机制能自动为我们做这些事情,所以我们在一般情况下是不需要自己来手工释放的。

    有时当撤消一个对象时,需要完成一些操作。例如,如果一个对象正在处理的是非Java 资源,如文件句柄或window 字符字体,这时你要确认在一个对象被撤消以前要保证这些资源被释放。为处理这样的状况,Java 提供了被称为收尾(finalization )的机制。使用该机制你可以定义一些特殊的操作,这些操作在一个对象将要被垃圾回收程序释放时执行。

    要给一个类增加收尾(finalizer ),你只要定义finalize ( ) 方法即可。Java 回收该类的一个对象时,就会调用这个方法。在finalize ( )方法中,你要指定在一个对象被撤消前必须执行的操作。垃圾回收周期性地运行,检查对象不再被运行状态引用或间接地通过其他对象引用。就在对象被释放之 前,Java 运行系统调用该对象的finalize( ) 方法。

      finalize()方法的通用格式如下:

      protected void finalize( )

    {

    // finalization code here

    }

      其中,关键字protected是防止在该类之外定义的代码访问finalize()标识符。该标识符和其他标识符将在第7章中解释。

      理解finalize( ) 正好在垃圾回收以前被调用非常重要。例如当一个对象超出了它的作用域时,finalize( ) 并不被调用。这意味着你不可能知道何时——甚至是否——finalize( ) 被调用。因此,你的程序应该提供其他的方法来释放由对象使用的系统资源,而不能依靠finalize( ) 来完成程序的正常操作。

      注意:如果你熟悉C++,那你知道C++允许你为一个类定义一个撤消函数(destructor ),它在对象正好出作用域之前被调用。Java不支持这个想法也不提供撤消函数。finalize() 方法只和撤消函数的功能接近。当你对Java 有丰富经验时,你将看到因为Java使用垃圾回收子系统,几乎没有必要使用撤消函数。

    finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.

    finalize()在什么时候被调用?

    有三种情况

    1.所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候.

    2.程序退出时为每个对象调用一次finalize方法。

    3.显式的调用finalize方法

    除此以外,正常情况下,当某个对象被系统收集为无用信息的时候,finalize()将被自动调用,但是jvm不保证finalize()一定被调用,也就是说,finalize()的调用是不确定的,这也就是为什么sun不提倡使用finalize()的原因

    有时当撤消一个对象时,需要完成一些操作。例如,如果一个对象正在处理的是非Java 资源,如文件句柄或window 字符字体,这时你要确认在一个对象被撤消以前要保证这些资源被释放。为处理这样的状况,Java 提供了被称为收尾(finalization )的机制。使用该机制你可以定义一些特殊的操作,这些操作在一个对象将要被垃圾回收程序释放时执行。

    要给一个类增加收尾(finalizer ),你只要定义finalize ( ) 方法即可。Java 回收该类的一个对象时,就会调用这个方法。在finalize ( )方法中,你要指定在一个对象被撤消前必须执行的操作。垃圾回收周期性地运行,检查对象不再被运行状态引用或间接地通过其他对象引用。就在对象被释放之 前,Java 运行系统调用该对象的finalize( ) 方法。

    finalize()方法的通用格式如下:

    protected void finalize( )

    {

    // finalization code here

    }

    其中,关键字protected是防止在该类之外定义的代码访问finalize()标识符。该标识符和其他标识符将在第7章中解释。

    理解finalize( ) 正好在垃圾回收以前被调用非常重要。例如当一个对象超出了它的作用域时,finalize( ) 并不被调用。这意味着你不可能知道何时——甚至是否——finalize( ) 被调用。因此,你的程序应该提供其他的方法来释放由对象使用的系统资源,而不能依靠finalize( ) 来完成程序的正常操作。

    注意:如果你熟悉C++,那你知道C++允许你为一个类定义一个撤消函数(destructor ),它在对象正好出作用域之前被调用。Java不支持这个想法也不提供撤消函数。finalize() 方法只和撤消函数的功能接近。当你对Java 有丰富经验时,你将看到因为Java使用垃圾回收子系统,几乎没有必要使用撤消函数。

    垃圾收集器在进行垃圾收集的时候会自动呼叫对象的finalize方法,用来进行一些用户自定义的非内存清理工作,因为垃圾收集器不会处理内存以外的东西。所以,有的时候用户需要定义一些清理的方法,比如说处理文件和端口之类的非内存资源。

  • 相关阅读:
    高可用网站多点部署架构实战经验总结
    使用阿里云发送邮件完美解决 端口 25 465
    阿里云 SSL 证书 总结
    支付宝支付接入流程
    阿里云服务器ECS的环境部署和安装
    对接 第三方物流APP 手机版
    智付支付接入心得
    二次开发本地配置域名
    从1到n整数中1出现的次数:O(logn)算法
    MongoDB——增删改查
  • 原文地址:https://www.cnblogs.com/hyp5490-/p/5542292.html
Copyright © 2011-2022 走看看