java学习注意点

来源:互联网 发布:软件开发工作量估算 编辑:程序博客网 时间:2024/05/16 01:57
switch语句中的case 语法中不能在不同的case中定义相同的变量
例如:
swictch(num){
  case 1:
    int number =1;
    break;
  case 2:
     int number =2;
     break;
     }(错误)
  这是错误的原因是,它可能会执行case 1,2但case后面没有break语句时(如果num=1)
  。而在java中一个变量定义多次是错误语法。

类中的一般方法调用:即发送消息给对象。编译器做了一些后台工作,它暗自把“所操作的对象的应用”作为第一个参数传入给要掉用的一般方法(非static修饰的)

在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布与方法定义之间,它们还会在任何方法(包括构造器)被调用之前得到初始化

数组的创建是在运行时刻进行的。

final数据
 1.一个永不改变的编译时常量
 2.一个在运行时被初始化的值,而且你不希望它被改变

空白final:
所谓空白final是指被声明为final但又未给定初始的域。无论什么情况,编译器都要确保空白final在使用前必须被初始化
 必须在域的定义处或者每个构造器中用表达式对final进行赋值,这正是在使用前总是被初始化的原因所在

final方法:
  把方法锁定,以防任何继承类修改它的含义。
类中所有的private方法都隐式的指定为是final

覆盖只是在某方法是基类的接口的一部分时才出现。即,必须能将一个对象向上转型为它的基本类型并调用相同的方法。
如果某个方法为private,它就不是基类的接口的一部分。它仅是一些隐藏于类中的程序代码,只不过是具有相同的名称而已。

由于final类禁止继承,所以final类中所有的方法都隐式指定为是final,因为它们无法覆盖。

多态的作用是消除类型之间的耦合关系。(动态绑定,后期绑定,运行时绑定)

方法绑定:将方法调用同一个方法主体关联起来被称作绑定。

前期绑定:在程序执行前进行绑定
后期绑定:在运行时根据对象的类型进行绑定。

java中除了static方法和final方法(private方法属于final方法)之外,其它所有的方法都是后期绑定。

StringBuffer 中append(a+":"+c),那编译器就会掉入陷阱,从而为你另外创建一个StringBuilder对象处理括号内的字符串操作。

随机访问文件:
RandomAccessFile rf = new RandomAccessFile(fileName ,"r");
RandomAccessFile rf = new RandomAccessFile(fileName, "rw");
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。  
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。  
"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
主要方法:
 String readUTF() 从此文件读取一个字符串。
 long getFilePointer()  返回此文件中的当前偏移量。
 long length()  返回此文件的长度。
 int read() 从此文件中读取一个数据字节。
 int read(byte[] b) 将最多 b.length 个数据字节从此文件读入 byte 数组。
 int read(byte[] b, int off, int len) 将最多 len 个数据字节从此文件读入 byte 数组。
 boolean readBoolean() 从此文件读取一个 boolean。
 byte readByte()  从此文件读取一个有符号的八位值。
 char readChar() 从此文件读取一个字符。
 double readDouble() 从此文件读取一个 double。
 float readFloat() 从此文件读取一个 float。
 void readFully(byte[] b) 将 b.length 个字节从此文件读入 byte 数组,并从当前文件指针开始。
 void readFully(byte[] b, int off, int len) 将正好 len 个字节从此文件读入 byte 数组,并从当前文件指针开始。
 int readInt() 从此文件读取一个有符号的 32 位整数。
 String readLine() 从此文件读取文本的下一行。
 long readLong() 从此文件读取一个有符号的 64 位整数。
 short readShort() 从此文件读取一个有符号的 16 位数。
 int readUnsignedByte() 从此文件读取一个无符号的八位数。
 int readUnsignedShort() 从此文件读取一个无符号的 16 位数。  
 void write(byte[] b)  将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。
 void write(byte[] b, int off, int len) 将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。
 void write(int b) 向此文件写入指定的字节。
 void writeBoolean(boolean v) 按单字节值将 boolean 写入该文件。
 void writeByte(int v) 按单字节值将 byte 写入该文件。
 void writeBytes(String s) 按字节序列将该字符串写入该文件。
 void writeChar(int v)  按双字节值将 char 写入该文件,先写高字节。
 void writeChars(String s) 按字符序列将一个字符串写入该文件。
 void writeDouble(double v) 使用 Double 类中的 doubleToLongBits 方法将双精度参数转换为一个 long,然后按八字节数量将该 long 值写入该文件,先定高字节。
 void writeFloat(float v)  使用 Float 类中的 floatToIntBits 方法将浮点参数转换为一个 int,然后按四字节数量将该 int 值写入该文件,先写高字节。
 void writeInt(int v) 按四个字节将 int 写入该文件,先写高字节。
 void writeLong(long v) 按八个字节将 long 写入该文件,先写高字节。
 void writeShort(int v) 按两个字节将 short 写入该文件,先写高字节。
 void writeUTF(String str) 使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。
 void seek(long pos) 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
 void setLength(long newLength) 设置此文件的长度。
 int skipBytes(int n)  尝试跳过输入的 n 个字节以丢弃跳过的字节。


多线程和线程池中的使用:
jion()把使开启这个线程的主线程等待该线程执行完再执行jion()下的语句该语句为阻塞方法。
例如在main方法中开启一个线程然后调用这线程的jion()使Thread-0(主线程)等待这线程执行完
线程池中的
invokeAny(Collection<? extends Callable<V>)其方法返回结果为V
它是获取最先执行完的线程并结束其它线程

invokeAll<Collection<? extends Callable<V>)其方法返回结果为List<Future<V>>
它是获取执行完所有的结果

成员变量和静态变量的区别
1.成员变量所属于对象,所有也称为实例变量
  静态变量所属于类,所有也称为类变量
2.成员变量存在于堆内存中
  静态变量存在于方法区中
3.成员变量随对象创建而存在。随着对象被回收而消失
  静态变量随着类的加载而存在,随着类的消失而消失
4.成员变量只能被对象调用
  静态变量可以被对象调用,也可以被类名调用

成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。

静态代码块:可以完成类的初始化。静态代码块随着类的加载而执行,而且只执行一次(new 多个对象就只执行一次)如果和主函数在同一个类中,优先与主函数执行

静态代码块,构造代码块,构造函数同时存在时执行顺序:静态代码块->构造代码块->构造函数

final在成员变量上的使用,它必须在构造方法中或者之前显示赋值,它不会默认赋值

接口是抽象方法和常量值组成的一个数据结构。(既然是常量就没有必要为每个对象分配一个特定的空间,为所有对象分配一个共同的存储空间)
 
-----------------------------------------------虚拟机初始化制------------------
java虚拟机实现必须在每个类或接口首次主动使用时初始化。
下面6种情况符合主动使用要求
1.当创建某个类的新实例时,或者通过在字节码中执行new指令,或者通过不明确的创建,反射,克隆,或反序列化时。
2.当调用某个类的静态方法时
3.当使用某个类或字段的静态字段,或者对该字段赋值时。
4.当调用java API中的某些反射方法。
5.当虚拟机启动时某个被表明为启动类的类,即含有main()方法的那个类
6.当初始化某个类的子类。即当初始化某个类时,要求它的超类已经被初始化了。

初始化:在此期间,都将给类变量赋以适当的初始值

所有的类变量初始化语句和类型的静态初始化器都被java编译器按顺序收集在一起,放到一个特性的方法中,称为类初始化方法。<clinit>通常的java程序方法是无法调用这个<clinit>方法的,只能被java虚拟机调用。

初始化一个类的过程包含以下两个步骤:
1.如果类存在直接超类的话,且直接超类还没有初始化,则先初始化直接超类。
2.如果类存在一个类初始化就执行此方法

并不是所有的类都需要在它的class文件中有个<clinit>()方法。如果类没有声明任何类变量,也没有静态初始化语句,那么它就不会有<clinit>()方法。


static{
index=100;
}
static int index =10;
public static void main(String[] args){
System.out.println(index);
}

}
//10

-----------------------------------ForkJionPool---------------------------------
为了实现Fork/Join任务,需要实现一个以下两个类之一的子类
ForkJionPool在mian中调用运行时它们和主线程是异步执行

RecursiveAction 用于任务没有返回结果
RecursiveTask  用于任务有返回结果

public class ForkDemo {

    public static void main(String[] args) {
            ProductListGenerator generator = new ProductListGenerator();
            List<Product> list = generator.generate(1000);
            for(int i=0;i<list.size();i++)
                System.out.print(list.get(i).getPrice()+ "  ");
                System.out.println();
            Task task = new Task(list,0,list.size(),0.20);
            
            ForkJoinPool pool = new ForkJoinPool();
            pool.execute(task);
            //异步任务没做完之前做其他事情
            while(!task.isDone()){
              try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            }
                for(int i=0;i<list.size();i++){
                    System.out.print(list.get(i).getPrice()+ "  ");
                }
            

    }

}

class Product{
    private String name;
    private double price;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    
}

class ProductListGenerator{
    public List<Product> generate(int size){
        List<Product> ret = new ArrayList<Product>();
        for(int i=0;i<size;i++){
            Product product = new Product();
            product.setName("Product"+i);
            product.setPrice(10);
            ret.add(product);
        }
        return ret;
    }
}

class Task extends RecursiveAction{

        //声明这个类的serialVersionUID属性
    private static final long serialVersionUID = 1L;
    private List<Product> products;
    private int first;
    private int last;
    private double increment;
    
    public Task(List<Product> products,int first,int last,double increment){
        this.first = first;
        this.products = products;
        this.last = last;
        this.increment = increment;
    }

    @Override
    protected void compute() {
        if(last-first<10)
            updatePrices();
        else{
            int middle = (last+first)/2;
            Task t1 = new Task(products,first,middle,increment);
            Task t2 = new Task(products,middle+1,last,increment);
            invokeAll(t1,t2);
        }
        
        
    }
    private void updatePrices() {
         for(int i=first;i<=last;i++){
             Product product = products.get(i);
             product.setPrice(product.getPrice()*(1+increment));
         }
            
    }
    
}

可以定以抽象类数组?
因为抽象类数组创建后它默认为null具体的实现要你自己创建子类放入

java虚拟机在调用成员方法时它会隐式传人this对象进去(即它会多一个参数this(当前对象))

匿名类和匿名内部类的区别?
匿名类
形式为:new <类或接口> <类的主体>
匿名内部类:
new ParentName(){
...// 内部类的定义
}

-------------------------------super和this异同---------------------------------
4.super和this的异同:
1)super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
2)this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
3)super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名    super.成员方法名(实参)
4)this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
5)调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
6)super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
7)super()和this()均需放在构造方法内第一行。
8)尽管可以用this调用一个构造器,但却不能调用两个。
9)this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
10)this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
11)从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。


-----------------------------static 的属性及方法--------------------------------
无论是静态属性还是静态方法它在类中所有对象都共用相同的一份空间。它与对象无关
如果你在类中使用的方法不需要体现多态,不需要访问成员属性就用静态方法因为它消耗的空间最小。


--------------------外部类中为什么可以通过创建内部类实例直接访问内部类中属性--
外部类不能直接访问内部类变量,必须通过创建内部类实例的方法访问,new InnerClass(32).m就是创建内部类实例访问内部类成员变量。你想不通的肯定是指内部类的私有变量怎么可以被外部类访问吧,按常规,私有变量m只能在InnerClass里被访问,但你要注意,内部类就相当于一个外部类的成员变量,举个例子。
class Outer{
private int m;
private class Inner{
private int n;
private int k;
  }
}
m和类Inner都是成员变量,他们之间是平等的,唯一不同的就是Inner它是包装了几个成员变量比如n,k,也就是说m n k是平等的,区别在于访问n k要通过Inner,就是要建立Inner实例访问n,k,

class Outer{
  private static class Inner{
    private static Outer instance = new Outer();
  }
  private Outer(){}
  public static Outer getInstance(){
    return Inner.instance;
  }
}
同时外部类也可以通过创建内部类的实例来访问内部类的私有成员方法,可以通过内部类.方法名(参数列表)来调用内部类中私有的静态方法



工作中调用别人逻辑时加上一个check()方法检查传人数据是否合法,提高程序的健壮性



------------------使用反射时注意传人参数为基本类型-----------------------
 /**
    * 注意点:在方法参数类表中如果为int类型时不能使用 参数.getClass()
    * 正确做法
    *  if(params[i] instanceof Integer)
    *      types[i] = int.class;
    *   基础类型时要进行判断,我这里用的是Object来接受的参数它会把基本类型包装为引用类型
    * 所以在获取其class时要记得把它转换为基础类型的class 如Integer类型 要变为int.class
 * @param clazz 执行方法所在的类的class类型
 * @param methodName 执行的方法名称
 * @param isStatic 是否为静态类型
 * @param params 参数列表
 * @throws Exception
 */
public static void getMethodUseTime(Class clazz,String methodName,boolean isStatic,Object... params ) throws Exception{
                  long startTime = System.currentTimeMillis();
                  
                  //首先要初始化实例为null因为可能测试的方法为static
                  Object instance = null;
                  //如果要测试的方法是成员方法创建实例
                    if(!isStatic)      
                  instance = clazz.newInstance();
                  int len = params.length;
                  //如果传人的参数列表为0表名这个方法没有参数
                  if(len==0){
                     Method method = clazz.getDeclaredMethod(methodName, null);
                     method.invoke(instance, (Object[])null);
                  }else{
                      //用来保存传人参数的类型
                    Class<?>[] types = new Class[len];
                      for(int i=0;i<len;i++){
                         if(params[i] instanceof Integer)
                             types[i] = int.class;
                         else if(params[i] instanceof Character)
                             types[i] = char.class;
                         else if(params[i] instanceof Double)
                             types[i] = double.class;
                         else if(params[i] instanceof Float)
                             types[i] = float.class;
                         else
                             types[i] = params[i].getClass();
                        
                      }
                      Method method = clazz.getDeclaredMethod(methodName,types);
                      method.invoke(instance, params);
                  }
                  
                  long endTime = System.currentTimeMillis();
                  System.out.println(methodName+",这个方法执行用时:"+(endTime-startTime)+"ms");
   }
}


注意:我的参数列表可以传人自定义的类型,但不能是方法中参数是用接口声明而传人的参数为实现类型。。。。。。
提供解决思路:
   可以使用 Class中提供的getSuperClass() 如果它没继承别的类 会返回Object 继承别的类返回继承的类
   可以使用 Class中提供的getInterfaces() 如果它实现了接口 它会返回所有它实现的接口 类型为Class数组

栈内中存放引用类型变量时它是存放堆中对象的首地址,它的大小由它的成员属性变量的大小即个数决定

--------------------------------String--------------------------------------------
String中有两种创建对象的方法
1,String str ="dddd";
2, String str2 =new String("ddd");
它们之间的区别?
使用第一种,它首先到方法区中的字符串池中查找看要创建的字符串是否已经存在,如果存在就直接使用(把栈中的引用变量指向方法区中的那个字符串)
没有会在方法区中的字符串池中创建并把栈中的引用变量指向它

使用第二种,它会先在执行上面的步骤。然后再在堆中创建一个字符串并把堆中引用变量执行堆中的那个字符串。

总结:它们存在地方不同。。。一个在方法区中的字符串池中,一个中堆中

intern()方法:它的返回是方法区中字符串池中那个字符串的地址。
    运行原理: 它先到方法区中的字符串池中去查找是否存在这样的字符串。有就返回这个字符串在方法区中字符串池中的地址。没有就先在方法区中的字符串池中创建,然后返回这个字符串在方法区中字符串池中的地址。。

String的字符创建一旦创建,这个字符串的内容就不会改变,从String 的源码可知它所有的内容放在一个char[] 的数组中
private final char[] value;一当它在构造方法中被初始化就不能改变。(确切的说是它的引用不能改变)
   public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }



-------------------------------null--------------------
String str = null;
String str2 = null;
在这里str==str2为真

---------------------------StringBuilder----------------
如果你已经知道最总的字符串大概的长度是多少,那就预先指定StringBuilder的大小可以避免多次重新分配缓冲。
如果你想走捷径,例如append(a+":"+c),那编译器就会掉入陷阱,从而为你另外创建一个StringBuilder对象处理括号内的字符串操作。
常用操作方法:
    insert(),replace(),substring(),reverse(),append(),delete();



-------------------------复写toString可能的死递归---------
class Outer{
   public String toString(){
     return "Outer"+this+"run";
  }
}
如果这样会死掉:
  Outer outer = new Outer();
  System.out.println(outer);
原因:当进行输出时对象会默认调用toString的方法
      但一个字符串+对象连接时它会偷偷把对象转换为String类型如何转换了,它会掉用对象上的toString方法

-------------------------instanceof使用注意点-------------------
instanceof只能用与动态中判断
class Fu{

}
class Children extends Fu{

}
列如: Fu fu = new Children();
if(fu instancof Children)
   Children children = (Children)fu;
不能什么判断都可以用instanceof如果A和B没半毛关系,编译是不会通过
总结:
   instanceof,要判断一个对象是否是某个类,前提是在使用多态定义时左边的类型要是instanceof右边类型的子类或着是孙子类。。。。
 
-------------------------------------复写equals方法---------------------------
    @Override
    public boolean equals(Object obj) {
        if (this == obj)//先比较这两个对象是否是同一个对象(即地址相同)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())//判断它们是否是同一个类型(同一类型它们的class对象只有一个,因为它只加载内存一次)
            return false;
        Teacher other = (Teacher) obj;
        if (addr == null) {
            if (other.addr != null)
                return false;
        } else if (!addr.equals(other.addr))//注意这行中的equals是String对象上的
            return false;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }


-----------------------------hashCode方法复写---------------------------
    @Override
    public int hashCode() {
        final int prime = 31;//先设置一个初始的int值
        int result = 1;//设置一个初始结果
        result = prime * result + ((addr == null) ? 0 : addr.hashCode());//如果属性为String类型就判断为null 返回0否则调用它上面的hashCode
        result = prime * result + age;//基本类型直接加上去
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
------------------String类中的hashCode方法复写----------------------------
  public int hashCode() {
        int h = hash;//初始一个值
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];//把结果先*32 +这个字符对应的Unicode编码表中的int值
            }
            hash = h;
        }
        return h;
    }


----------------------------------------泛型--------------------
编译器只能做语法上检查,不能做语义上检查。

泛型:
    不是泛型错误只能在运行时期发现,而泛型可以把错发降到编译时期。
    泛型只能帮你做数据类型的检查,不能完成逻辑,在运行时期泛型发生擦除
    只在编译时期有效,在.class文件中泛型已经消失掉


 什么是泛型?
  泛型定义者:在编译期不确定数据类型,它是泛型持有者
  泛型使用者: 在使用泛型时必须明确类型

 注意:要是想使用在泛型上加上限制可以使用如下
   public static <T extends Fu1> void show(T t){
           t.show();
  }
  它只能传人Fu或者它的子类。



----------------------------效率高的---------------------------------
取半时(返回整数) mumber>>1
奇偶判断  if((numer&1)==1)为奇 else 为偶
>>,<<它和muber*2^n作用相同





-----------------如何通过反射获取泛型类上的泛型---------------------
注意:只能先用一个子类来继承它才能实现。
 abstract class A<T>{
  private Class<T> clazz;
  void doGetClass(){
    Type genType = this.getClass().getGenericSupperClass();
    Type[] params =((ParameterizedType)genType).getActualTypeArguments();
    this.clazz = (Class<T>)params[0];
    }
 }

只有实例化了T的子类才可以按上述方法获取T,如果子类没有实例化,是无法获取到
  class B<T> extends A<T>{
   }

由于class B并没有实例化T,所以下面代码拿不到String.class
  B<String> b = new B<>();
  b.doGetClass();//拿不到String

正确做法:
  class B extends <String>{}

B按下面声明,下面代码可以拿到String.class
   B b = new B();
   b.doGetClass();



-----------------------------------class文件产生的情况--------------------
只有是用class 的就会产生一个class文件
内部类: 外部类名$内部类名.class
普通类: 外部类名.class


-----------------------在对ArrayList删除操作时它的元素会向前充-------------
如果你对ArrayList进行按索引删除,它会把后面元素向前移动。。所有你每次remove(0)都可以删掉所有的元素


---------------------------------慢慢理解-----------------------------------
private Node<K,V>[] tabs = (Node<K,V>[]) new Node[16];//正确

static class Node<K,V>{
        K key;
        V value;
        Node<K,V> next;
        
        public Node(K key,V value,Node<K,V> next){
            this.key = key;
            this.value =value;
            this.next = next;
        }
        
    }
在堆内存中创建对象时要明确数据类型,如果不明确无法编译通过。
数组中的每个大小等于它类型大小。像上面那个语句它会在每个数组开辟两个存放应用类型的大小的位置
T[] array = new T[1024];//编译失败
上面不能写出:
private Node<K,V>[] tabs =new Node<K,V>[16];//错误new 的后面不能出现泛型,有就编译失败


-----------------------------在调用Collections.sort()------------------------
在调用Collections.sort()当传人的参数为一个时它会调用list中对象类上的compareTo方法
所有你的类必须要实现Comparable





----------------------------动态的改变排序法则------------------------------
1.
 使用注入方式:
interface Compater {
    int compare(Object o1,Object o2);
}
-------------------------------------------------------------------
public class Food implements Comparable<Food>{//这里使用代理的改造(一般代理是代理类成员属性持有真实类的实例,这里是静态属性持有真实类的实例)
    private static Compater compater;
    private double price;
    private int grade;
    private String name;
    
    public String toString() {
        return  " price=" + price + ", grade="
                + grade + ", name=" + name + "]";
    }
   ............

    public Food() {
        super();
        // TODO 自动生成的构造函数存根
    }

    public Food(double price, int grade, String name) {
        super();
        this.price = price;
        this.grade = grade;
        this.name = name;
    }

    public static void setCompater(Compater compater){
              Food.compater = compater;
    }

    @Override
    public int compareTo(Food o) {
        if(compater==null)
        return 0;
        else
        return compater.compare(this, o);
    }
}

---------------------------------------------------------------
public class PriceCompater implements Compater {

    @Override
    public int compare(Object o1, Object o2) {
        Food food1=null;;
        Food food2=null;
        if(o1 instanceof Food)
          food1 =(Food)o1;
        if(o2 instanceof Food)
          food2 =(Food)o2;
        return (int)(food1.getPrice()-food2.getPrice());
    
}

}

2.使用Collections.sort()的有两个参数第二个传人一个Compator的实现


----------------------------文件的操作-----------------------------------
使用file.listFiles()
如果file为文件它调用这会返回null
如果file为目录但里面没文件时返回File[] 的长度为0([])不是null

//源码:
 public File[] listFiles() {
        String[] ss = list();
        if (ss == null) return null;
        int n = ss.length;
        File[] fs = new File[n];
        for (int i = 0; i < n; i++) {
            fs[i] = new File(ss[i], this);
        }
        return fs;
    }

 public String[] list() :
If this abstract pathname does not denote a directory, then this
     * method returns {@code null}.  Otherwise an array of strings is
     * returned, one for each file or directory in the directory.  Names
     * denoting the directory itself and the directory's parent directory are
     * not included in the result.  Each string is a file name rather than a
     * complete path.
     
  public static File[] listRoots():
  This method returns an array of {@code File} objects that denote the
     * root directories of the available filesystem roots.  It is guaranteed
     * that the canonical pathname of any file physically present on the local
     * machine will begin with one of the roots returned by this method.

---------------------------文件的删除-----------------------
方法一://这里写的很好
主要思想是:如果是目录让它继续进入它的子目录,完成这些才删除
  public static void deleteFile2(File path){
         //如果path为目录进入
       if(path.isDirectory()){
          File[] files = path.listFiles();
          for(File file:files)
              deleteFile2(file);
       }
        //执行完后删掉
       path.delete();
   }

方法二:
     public static void deleteFile(File path){
          File[] files = path.listFiles();
          if(files!=null&&files.length!=0){
              for(File file:files){
                  if(file.isDirectory())
                        deleteFile(file);
                           else
                             file.delete();
              }
          }
          path.delete();
   }
   


-----------------------------使用装饰者模式改变数据时---------------------
要注意当原数据为-1你也要放回-1
int temp = in.read();
if(temp==-1)
return -1;
else
 return temp+10;



---------------------------------动态代理--------------------------------------
JDK动态代理中包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
 
可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。
 
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
                               throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
 
Ps:类加载器
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。



----------------------------------自定义QueryRunner--------------------------
public MyQueryRunner{
  private DataSoure source;
  public MyQueryRunner(){
  }
  public MyQueryRunner(DataSource source){
    this.source = source;
    }
    
  public int update(String sql,Object... params) throws SQLException{
    Connection conn = source.getConnection();//获取连接池的连接
    PreparedStatement ps = conn.preparStatement(sql);
    ParameterMetaData metaData = ps.getParameterMetaData();
    int count = metaData.getParameterCount();
    if(count!=params.lenght)
     throw new RuntimeException("...");
     for(int i=1;i<=count;i++)
      ps.setObject(i,params[i-1]);
      int num = ps.executeUpdate();
      Dbutils.closeQuietly(conn,ps,null);
      return num;
      }
      

 public<T> T query(String sql,MyResultSetHander<T> rsh,Object... params) throws SQLException{
      Connection conn = source.getConnection();
      PreparedStatement ps = conn.prepareStatement(sql);
      ParameterMetaData metaData = ps.getParameterMetaData();
      int count = metaData.getParameterCount();
      if(count!=params.length)
      throw new RuntimeException("....");
      for(int i=1;i<=count;i++)
       ps.setObject(i,params[i-1]);
       ResultSet rs = ps.executeQuery();
       T t = rsh.handle(rs);
       Dbutils.closeQuitly(conn,ps,rs);
       return t;
       }
 }
 
 
 public interface MyResultSetHandler<T>{
   T handle(ResultSet rs)throws SQLException;
   }
   
   
   
   
   
-----------------------------------------------利用方法上的注解实现改造方法------------------------
public class BasicFactory{
  private static BasicFactory factory = new BasicFactory();
  private static Properties properties;
  private BasicFactory(){}
 
  static{
    try{
      properties = new Properties();
      properties.load(new FileReader(BasicFactory.class.getClassLoader().getResource("config.properties").getPath());
      }catch(Exception e){}
     }
     
  private BasicFactory(){}
 
  public <T extends Service> T getService(Class<T> clazz){
    try{
       String infName = clazz.getSimpleName();
       String implName = properties.getProperty(infName);
       final T service = (T)Class.forName(implName).newInstance();
       
       T proxyService = (T) Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),new InvocationHandler(){
                                           public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
                                                if(method.isAnnotationPresent(Tran.class)){
                                                     try{
                                                       TransactionManager.startTran();
                                                       Object obj = method.invoke(service,args);
                                                       TransactionManager.commit();
                                                       return obj;
                                                       }catch(Exception e){
                                                       TransactionManager.rollback();
                                                       throw new RuntimeException(e);
                                                       }finally{
                                                       TransactionManager.release();
                                                       }
                                                }else{
                                                 return method.invoke(service,args);
                                                }
                         });
   
   return proxyService;
  }catch(Exception e){
    throw new RuntimeException(e);
   }
 }
 
}




-------------------------实现事务控制分层解耦-------------------------------------
public class TransactionManager{
  private TransactionManager(){}
  private static DataSource source = new ComboPoolDataSource();
  private static ThreadLocal<Boolean> isTran_local = new ThreadLocal<Boolean>(){
                      protected Boolean initalValue(){
                          return false;
                          }
               };
  private static ThreadLocal<Connection> proxyConn_local = new ThreadLocal<Connection>(){};
  private static ThreadLocal<Connection> realConn_local = new ThreadLocal<Connection>(){};
 
  public static void startTran()throw SQLException{
      isTran_local.set(true);
      finnal Connection conn = source.getConnection();
      conn.setAutoCommit(false);
      realConn_local.set(conn);
      
      Connection proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),conn.getClass().getInterfaces(),new InvocationHandler(){
                                                                  public Object invoke(Object proxy,Method method,Object[] params)throws Throwable{
                                                                                  if("close".equals(method.getName()){
                                                                                         return null;
                                                                                     }else{
                                                                                     return method.invoke(conn,args);
                                                                                     }
                                                                                    }
                                                                                });
     public static void Commit(){
       DbUtils.commitAndCloseQuietly(proxyConn_local.get());
       }
       
     public static void rollback(){
       DbUtils.rollbackAndCloseQuietly(proxyConn_local.get());
       }
       
       
    public static DataSource getSource()throws SQLException{
       if(isTran_local.get())//判断是否开启同一事务控制
       return (DataSource)Proxy.newProxyInstance(source.getClass().getClassLoader(),source.getClass().getInterfaces(),new InvocatinHandler(){
                                           public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
                                                if("getConnection".equals(method.getName()){
                                                    return proxyConn_local.get();
                                                   }else{
                                                   return method.invoke(source,args);
                                                   }
                                                }
                                          });
       else
         return source;
    }
   
   
    public static void release(){
           DbUtils.closeQuietly(realConn_local.get());//真正的关闭数据库中的连接池中连接
           realConn_local.remove();
           proxyConn_local.remove();
           isTran_local.remove();
        }
   
   }
                                          
                 
-------------------------------java锁机制判断时注意用while if判断会有问题--------------------   





--------------------------->>   和 >>> 的异同点---------------------------------

>>表示是带符号的右移
>>>表示无符号的右移

带符号右移(>>):按照二进制把数字右移指定数位,高位如符号位为正补零,符号位负补一,低位直接移除
无符号的右移(>>>):按照二进制把数字右移指定数位,高位直接补零,低位移除!                                       




-------------------------使用序列化对象和反序列化时如果是在原有文件上添加数据会出差------------------------
 反序列化错误:java.io.StreamCorruptedException: invalid type code: AC    
 问题是ObjectOutputStream是不能直接在一个文件后追加东西的,直接追加的话,每次追加时都会多写一个header进去,导致在反序列化的时候出现标题所说的异常。
  解决方案也很简单:就是追加的时候不写那个header就行了吧。

以下是我从网上搜到的答案,记录下来。



Here's the trick: subclass ObjectOutputStreamand override the writeStreamHeadermethod:


//使用继承复写它的writeStreamHeader方法
public class AppendableObjectOutputStream extends ObjectOutputStream{

public AppendableObjectOutputStream(OutputStream out)throws Exception{
super(out);
}

@Override
protected void writeStreamHeader()throws IOException{//将写入头的方法改为空实现
// do not write a header
}

}

To use it, just check whether the history file exists or not and instantiate either this appendable stream (in case the file exists = we append = we don't want a header) or the original stream (in case the file does not exist = we need a header).

Edit

I wasn't happy with the first naming of the class. This one's better: it describes the 'what it's for' rather then the 'how it's done'

具体使用:如果第一次使用来写入对象序列化时使用ObjectOuputStream因为必须要存在一个头 new FileOutputStream(String path,boolean true);
以后的使用使用自己改造后的AppendableObjectOutputStream 它不会再重新写入头

改造结和上面一个我们上面改进的ObjectOuputStream利用多态来来自动选择是否加头
class MyObjectOutputStream{
    private File file;//用来记录文件
    private boolean exit;//用来判断是否存在
    public MyObjectOutputStream(String path) throws Exception{
        File file = new File(path);
        if(file.exists())//如果文件存在就修改标识
            exit=true;
        else
            file.createNewFile();//不存在就创建
        this.file = file;
    }
    
    public ObjectOutputStream  getOutputStream() throws FileNotFoundException, IOException{
        if(exit)//根据存在的标识符来选择返回的类型
            return new AppendableObjectOutputStream(new FileOutputStream(file,true));
        return new ObjectOutputStream(new FileOutputStream(file,true));
    }
}




-------------------------------------使用序列化是读取所有的数据巧妙利用确定异常--------------------------------------------------------
在使用 ObjectInputStream,  while(true){ readObject()} 它读到数据的末尾时会抛出EOFException 利用这个异常我们可以在catch抓取不抛
try{
  while(true)
   readObject();

}catch(EOFException e){
   System.out.println("读到数据的最后了,没有数据可读!");
 }catch(Exception e){
   e.printStack();
 }    



---------------------------注解,是可以继承,即在接口的方法上定以一个注解在其实现类上它就有了这个注解------
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Tran {

}


-------------------------------在判断某对象是否为空时一般为null==instance-----------------------
一般常量都会写在左边,这是因为==和=比较相近,如果误写成“=”,那么常量写在左边的会报错,
 而,常量写在右边的不会报错。(这也是常量为什么写在左边它是防止你少写个=变成赋值,如果写在左边程序就会提示你错误,因为常量是不能改变的。写在右边则会把逻辑错
误带到运行中去)



---------------------------- 如何实现给不同对象实现同步操作---------------------------------
可以使用相同字节码对象锁
public class MyBlock {
    private String name;
    
     public MyBlock(String name){
         this.name = name;
     }
    
     public void show(){
         synchronized(MyBlock.class){
            while(true){
            try {
                Thread.sleep(1000);
                 System.out.println(name+":我运行啦");
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            
            }
         }
     }
}


------------------------如何实现停止某个线程-------------------------------------------------
class StopRunnable implements Runnable{
   boolean stopFlag;
   public void setStopFlag(boolean stopFlag){
         this.stopFlag = stopFlag;
   }
 
   public void run(){
      while(true){
         ......
         if(stopFlag)
           break;
         .........
     }
  }
}



------------------------------java虚拟机中内存区域--------------------------------------------------
程序计数器:
程序计数器(program counter register)只占用了一块比较小的内存空间,至于小到什么程度呢,这样说吧,有时可以忽略不计的。
作用
可以看作是当前线程所执行的字节码文件(class)的行号指示器。在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要这玩意来实现的。
特性
因为处理器在一个确定是时刻只会执行一个线程中的指令,线程切换后,是通过计数器来记录执行痕迹的,因而可以看出,程序计数器是每个线程私有的。
如果执行的是java方法,那么记录的是正在执行的虚拟机字节码指令的地址的地址,如果是native方法,计数器的值为空(undefined)。
这个内存区域是唯一一个在java虚拟界规范中没有规定任何OutOfMemoryError的情况的区域。至于为什么没有这个异常呢,要是一个计数的功能在出这个异常。

java虚拟机栈:
与程序计数器一样,java虚拟机栈也是线程私有的,它的生命周期和线程相同。
虚拟机栈描述是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放了编译器可知的各种基本数据类型,对象引用类型(reference类型,它不等同与对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)returnAddress类型(指向了一条字节码指令的地址)

本地方法栈:
虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用native方法服务、

java堆:
java堆是java虚拟机所管理的内存中最大的一块。java堆是所以线程共享的一块内存区域,是虚拟机启动时创建。此内存域的唯一目的就是存放对象实例(即使用new)
java堆是垃圾器管理的主要区域,java堆可以处于物理上不连续的内存空间中,只要逻辑上连续。

方法区:
方法区与java堆一样,是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

运行时常量池:
运行时常量池是方法区的一部分。Class文件中除了有类的版本,字段,方法,接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放
运行时常量池相对与Class文件常量池的另外一个重要特征是具备动态性,java语言并不要求常量一定只有编译期才能产生,也就是并非与置入Class文件中常量池的内容才能就让方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法





---------------------------------------------数据结构上的排序------------------------------------------------------------
堆排序:
这里我以大根堆排序为例:
必要知识:1,索引为1为根节点  2,父亲节点的索引和孩子节点索引之间的关系   左孩子节点索引=父亲节点索引*2   右孩子节点索引=父亲节点索引*2+1
3,初始化堆要从后先前初始它们 4,最后就把索引为1与它后面的元素交换使后面的元素慢慢变得有序,交换一次就重新排堆,排时它的个数要减1

排序算法:
 堆排序的关键是构建初始堆。假设完全二叉树的某一个节点i,它的左子数,又子树已是堆,接下来需要将R【2i】与R【2i+1】之中的最大者与R【i】比较,若R【i】较小则将其与最大孩子的关键字交换,这可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到完全二叉树中节点i构成为堆为止。

private static void sift(int[] array,int beginIndex,int n){
           int i = beginIndex;//它表示从第几个数进行排序
           int j = i*2;//由二叉树的结构可知i的左孩子节点为i*2
           int temp = array[i];//将要排的节点先存起来
           
           while(j<n){
               if(j<n&&temp>array[j])//如果左孩子节点比其父亲节点小则将j指向它的右孩子节点
                    j++;
               if(temp<array[j]){//如果孩子比父节点大这交换。由于这会破坏交换的那个孩子节点的排序所有要对那孩子结进行这样操作
                   array[i]=array[j];
                   i=j;//将i指向它交换的那个孩子节点
                   j=2*i;//将j指向原来孩子节点的左孩子节点
               }else{
                   break;
               }
               
           }
           array[i]=temp;
    }
    
    
    public static void heapSort(int[] array,int n){//大根堆
        for(int i=n/2;i>=1;i--)
            sift(array,i,n);//初始化堆
        for(int i=n-1;i>=2;i--){//由于大堆上的根节点是最大的我可以将它和最后的节点交换下使数组后面越来越有序
            int temp = array[1];
            array[1] = array[i];//把后面的元素赋值给第一个元素
            array[i]=temp;//将后面的元素变为最大的
            sift(array,1,i-1);//重新进行排序不过参加排序的元素要减少
        }
    }

快速排序:

希尔排序:

冒泡排序:

选择排序:

插入排序:

归并排序:

基数排序:



---------------------------------单例模式下的易错区------------------
public static Singleton getInstance() {  
2.         if (instance == null) {  
3.             synchronized (instance) {  
4.                 if (instance == null) {  
5.                     instance = new Singleton();  
6.                 }  
7.             }  
8.         }  
9.         return instance;  
10.     }  
在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
 
a>A、B线程同时进入了第一个if判断
 
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
 
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
 
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
 
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

最好的解决办法是使有静态内部类


---------------------------读写锁的使用-------------------------------------------------
重入
此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入 reader 使用它们。

此外,writer 可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。如果 reader 试图获取写入锁,那么将永远不会获得成功。

锁降级
重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。

锁获取的中断
读取锁和写入锁都支持锁获取期间的中断。

Condition 支持
写入锁提供了一个 Condition 实现,对于写入锁来说,该实现的行为与 ReentrantLock.newCondition() 提供的 Condition 实现对 ReentrantLock 所做的行为相同。当然,此 Condition 只能用于写入锁。

读取锁不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException。

监测
此类支持一些确定是保持锁还是争用锁的方法。这些方法设计用于监视系统状态,而不是同步控制。

此类行为的序列化方式与内置锁的相同:反序列化的锁处于解除锁状态,无论序列化该锁时其状态如何。

示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):

 class CachedData {
   Object data;
   volatile boolean cacheValid;
   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        // Recheck state because another thread might have acquired
        //   write lock and changed state before we did.
        if (!cacheValid) {
          data = ...
          cacheValid = true;
        }
        // Downgrade by acquiring read lock before releasing write lock
        rwl.readLock().lock();
        rwl.writeLock().unlock(); // Unlock write, still hold read
     }

     use(data);
     rwl.readLock().unlock();
   }
 }

在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
class RWDictionary {
    private final Map<String, Data> m = new TreeMap<String, Data>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data get(String key) {
        r.lock();
        try { return m.get(key); }
        finally { r.unlock(); }
    }
    public String[] allKeys() {
        r.lock();
        try { return m.keySet().toArray(); }
        finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
        w.lock();
        try { return m.put(key, value); }
        finally { w.unlock(); }
    }
    public void clear() {
        w.lock();
        try { m.clear(); }
        finally { w.unlock(); }
    }
 }

实现注意事项:
此锁(ReentrantReadWriteLock)最多支持 65535 个递归写入锁和 65535 个读取锁。试图超出这些限制将导致锁方法抛出 Error。


-----------------------锁中Condition的使用--------------------------------------
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

实现注意事项
在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。

因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。

要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。

由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。





---------------------------------------线程中的几种状态-------------------------------------------------------------

    1.新建状态(New):
         当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
 
     2.就绪状态(Runnable)
 
        一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
 
        处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
 
    3.运行状态(Running)
 
        当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
 
    4. 阻塞状态(Blocked)
 
        线程运行过程中,可能由于各种原因进入阻塞状态:
         1>线程通过调用sleep方法进入睡眠状态;
         2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
         3>线程试图得到一个锁,而该锁正被其他线程持有;
         4>线程在等待某个触发条件;
         ......           
 
        所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
 
    5. 死亡状态(Dead)
 
        有两个原因会导致线程死亡:
         1) run方法正常退出而自然死亡,
         2) 一个未捕获的异常终止了run方法而使线程猝死。
         为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.




-----------------------------------------volatile--------------------------------
volatile让变量每次在使用的时候,都是从主存中获取,而不是各个的“工作内存"

背景:有个内存区域是jvm虚拟机栈,每个程序运行时都会有个线程,线程保存了线程运行时变量值信息。当线程访问某一个对象值得时候,首先通过对象的引用找到对象堆内存的变量的值,然后把堆内存变量的值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆上变量值有任何关系
而是直接修改副本变量的值,在修改完之后的某个时刻(线程退出之前),自动把线程变量副本的值写回到对象在堆中变量,这样在堆中的对象的值就发生变化。
(注意:这里主要是在改变变量时)
  主要步骤:
   1 read and load 从主存复制变量到当前工作内存
   2 use  and asign 执行代码,改变共享变量值
   3 store and write 用工作内存数据刷新主内存相关的内存
 但这些操作并不是原子性,也就是在read,load之后,如果主存count变量发生修改之后,线程工作内存中的值由于已加载,不会对产生对应的变化
  对应volatile变量,jvm虚拟机保证从主内存加载到线程工作内存的值最新的。


------------------------在使用同步操作时如何让用户操作体验最好-----------------------------------
我们可以先把用户的请求放入一个缓存中(List)再从里面一次取一个进行同步操作,这样会让用户只需点击一次余下的就让用户等待,而不用不停的等待。。。。。。

-----------------------------------Void----------------------------------
Void 类是一个不可实例化的占位符类,它持有对表示 Java 关键字 void 的 Class 对象的引用。


-----------------------------读写锁的降级的用--------------------------
在数据进行读操作时,采用读锁效率最高,但是有可能第一次读数据时。内存中还没有加载数据进来,此时就必须先将数据写入到内存中再读取。这时就需先释放读锁,来获取写锁。写完后修改标记在释放写锁之前会先获取读锁。
为什么要先获取读锁了再释放写锁?我的猜想是在释放写锁而你还没有获取读锁,cpu巧合在你释放写锁而还没获取读锁时这个线程的时间片用完。cpu又巧合调入了一个修改缓存数据的线程。。。你想下次再回到你那个线程时它返回的数据就是修改后的脏数据。。。。。


-------------------------lock中的用法-----------------------------
要养成
lock.lock();
try{
 }finally{
  lock.unLock();
}


-----------------数据库中的ResultSet中getMetaData()用法------------------
ResultSetMetaData rsd = rs.getMetaData() ;
getColumnClassName(i)//获取对应java中的类名。如果字段为varchar获取的值为java.lang.String
getColumnName(i)//获取字段上的名称
getColumnTypeName(i)//获取对应数据库中的名称如varchar
getTableName(i)//获取数据库名称
注意数据库中的i是从1开始


-------------------------使用反射来获取指定方法-------------------------
当为方法的参数设置值时可以使用将参数向上转型为Object 再设到方法上
ublic T handler(ResultSet rs) {
    try{
     T instance = clazz.newInstance();//获取类的实例
    ResultSetMetaData rsd= rs.getMetaData();//获取结果集上的源数据
    int count = rsd.getColumnCount();//获取源数据上的列数大小
    if(rs.next()){
        for(int i=1;i<=count;i++){
              String name = rsd.getColumnName(i);//获取指定列名称(name)
               name ="set" +name.substring(0,1).toUpperCase()+name.substring(1);//拼凑成setName形式
               String className =  rsd.getColumnClassName(i);//获取指定列名上的类型名(java.lang.String)
               Class clazz2 = Class.forName(className);//通过名称来获取Class对象
               Method method = clazz.getDeclaredMethod(name, clazz2);
               Object object = rs.getObject(i);
               method.invoke(instance, object);    
                }
        }
    return instance;
        }catch(Exception e){
            e.printStackTrace();
          throw new RuntimeException(e);
        }
    }



-------------------------------自己实现BeanListHandler------------------------
class BeanListHander<T> implements MyResultHandler<List>{

    private Class<T> clazz;
    
    public BeanListHander(Class<T> clazz){
        this.clazz = clazz;
    }
    
    @Override
    public List<T> handler(ResultSet rs) {
        List<T> list = new LinkedList<T>();//初始化一个List集合
        try{
            ResultSetMetaData rsd = rs.getMetaData();//获取结果集元数据
            int count = rsd.getColumnCount();//获取数据库中表的列数
            Method[] methods = new Method[count];//定义一个方法集合它的大小为列的长度
            for(int i=1;i<=count;i++){
                String name = rsd.getColumnName(i);//获取要调用方法的名称,这里只能获取它的后缀
                name = "set"+name.substring(0,1).toUpperCase()+name.substring(1);//拼接为一个完整的set方法
                String ps = rsd.getColumnClassName(i);//获取方法上的参数类型字符串(java.lang.String)
                Class pt =Class.forName(ps);//生成Class对象
                methods[i-1] = clazz.getDeclaredMethod(name, pt);//获取方法对象并放入方法数组中
            }
            
        while(rs.next()){
            T instance = clazz.newInstance();
            for(int i=1;i<=count;i++){
                Object object = rs.getObject(i);
                  methods[i-1].invoke(instance, object);
            }
            list.add(instance);
        }
        return list;
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
    
}


注意:这个中要你定义的类中例如User不能有基本类型如int,char,你必须为Integer。。还有一种办法是你可以先判断它是为Integer类型吗,如果是你就把它转换为int.class设置数据可以为setInt


------------------------------finally和return的执行顺序-----------------------
一般情况它是先执行finally最后执行return语句。


----------------------出现java.lang.IllegalMonitorStateException----------------
这个问题主要是:
 只能在同步控制方法或同步控制块里调用wait(),notify()和notifyAll()。如果在非同步控制方法里调用这些方法,程序能通过编译,但运行的时候,将得到IllegalMonitorStateException异常,并伴随着一些含糊的消息,比如"当前线程不是拥有者"。消息的意思是,调用wait(),notify()和notifyAll()的线程在调用这些方法前必须"拥有"对象的锁。

  可以让另一个对象执行某种操作以维护其自己的锁。要这么做的话,必须首先得到对象的锁。比如,如果要在对象x上调用notify(),那么就必须在能够取得x的锁的同步控制块中这么做:
如果是用Lock和Condition来实现唤醒与阻塞(使用await和signal(),signalAll()而不是notify()和wait())



----------------------------------线程池的用法详解-------------------------------
 
线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
 
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
 
long keepAliveTime, TimeUnit unit,
 
BlockingQueue<Runnable> workQueue,
 
RejectedExecutionHandler handler)
 
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
 
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。
 
当一个任务通过execute(Runnable)方法欲添加到线程池时:
 
l  如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
 
l  如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
 
l  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
 
l  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
 
l  当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
 
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
 
NANOSECONDS、

MICROSECONDS、
 
MILLISECONDS、
 
SECONDS。
 
workQueue常用的是:java.util.concurrent.ArrayBlockingQueue   
                   java.util.concurrent.LinkedBlockingQueue//它无界限
使用LinkedBlockingQueue核心池大小和最大池大小保池一致,因为它永远都无法装满。




---------------------------------------------ARP协议----------------------------------------------
ARP属于数据链路层协议,主要负责把IP地址对应到硬件地址。直接说吧,都怪交换机太“傻”,不能根据IP地址直接找到相应的计算机,只能根据硬件地址来找。于是,交换机就经常保留一张IP地址与硬件地址的对应表以便其查找目的地。而ARP就是用来生成这张表的。


-----------------------------我在使用网络编程是利用PrintWriter pw = new PrintWriter(System.out,true);遇到的问题----------------------
问题:我采用服务器在每获取一个客户端的socket时就开启一个新的线程来处理这个请求,我把PrintWriter pw = new PrintWriter(System.out,true)这句放入到try中自动关闭资源块中。第一个客户端来访问正常,但后面访问就在控制台上再也看不到打印的数据。。郁闷
解决办法: 我把PrintWriter pw = new PrintWriter(System.out,true)从自动关闭资源块中移除来。
原因: 由于你把最底层的输出流给关闭了,下次Sytem.out这个输出流为null但它不会抛异常它会假装很正常的往下执行完
源码分析:

//所有的println都会最终调用这个方法从这里可知道它很恶心的偷偷将它正常化
 public void write(String s, int off, int len) {
        try {
            synchronized (lock) {
                ensureOpen();
                out.write(s, off, len);
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }


--------------------------------网络编程中切不可随意把流关闭放入到try中自动关闭块中------------------------------------------
问题:我把OuputStream output = socket.getOutputStream();放入到try中资源自动关闭块中然后又显示的掉socket.shutdownOuputStream...后面就报异常了
感想:jdk7中自动关闭资源块做的这的不好,如果是我我会先去判断是否关闭没有就帮你关闭,有就不做任何事



---------------------------------------如何自定义类加载器--------------------------------
1. 复写findClass方法
2. 获取这个.class文件所对应的二进制流
3. 再在这个findClass最后返回 return defineClass(null, data, 0, data.length);
注意:这个findClass调用的源码:
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {//如果有父类加载器就让父类去加载
                        c = parent.loadClass(name, false);
                    } else {//如果没有父类加载器这表明它是启动类加载器用它来加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);//如果还没有早到就调用自己的findClass来加载

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }



----------------------使用数据库利用QueryRunner中的update方法---------------------------------
问题:我定义了一个事务,在使用使程序可以真确的运行但数据库有个没变化。最后发现自己传人的参数写反了
感触:参数写反它不会抛异常,所有在进行数据库数据修改时它不是有个返回值吗,我们可以利用它的返回值如果小于等于0我们可以手动的抛出一个异常


------------------------在使用数据库中进行个数查询时-----------------------------
查询记录的个数要用count(1) 它的效率比count(*)要好一些
注意:使用*时要注意它的效率最好不要使用*要明确指出它的字段


------------------eclipse中src的文件编译后自动赋值到bin目录下(即发布)------------------


------------------------java中的类被加载到内存中不受java虚拟机管它是整个程序结束才销毁-------------------
 类中的static 这样的数据也是这样,它们只加载一次。。利用它们的声明周期是整个项目即它只会加载一次可以把它们作为缓存
static变量在软件整个生命周期内都存在,是不会被GC回收的。
 不是类不被回收,类本身不占用内存,是类对象、静态类成员占用内存空间。
A static field keeps whatever object it refers to forever or until the AppDomain
 that the types are loaded into is unloaded. A common way to leak memory is to have a
 static field refer to a collection object and then to keep adding items to the collection object. The static field keeps the collection object alive and the collection object keeps all its
 items alive. For this reason, it is best to avoid static fields whenever possible.


----------------------java中有3种类加器--------------------
AppClassLoader 应用程序类加载器
作用:它负责加载用户类路径上指定的类库

ExtensionClassLoader 扩展类加载器
作用:它负责加载<JAVA_HOME>\lib\ext目录中的

BootstrapClassLoader 启动类加载器
作用:它负责加载存放在<JAVA_HOME>\lib


-------------------------------------------源码与编译后反遍后的代码-----------------------------
源码:
byte b = 12+3;
b+=2;

反编译后的代码
int i = 15;
i = (byte)(i+2);


--------------------------------为什么药"aaa".equals(name)来判断好些了----------------------
主要原因防止传入的name为nulll时会发生空指针异常是程序的健壮性更好


----------------------Sqlite数据库中注意点---------------------------------
Sqlite中的自动增长只能是integer类型而且是 autoincrement


------------------------javaweb中的路径---------------------------------
config.getServletContext().realPath("//");
由于ServletContext为项目的上下文对象它是链接服务器和你写的类的东西
所以它获取的目录就是部署到tomacat上的项目路径(...../day13/)


---------------------不同jdk下String对象上的intern--------------
产生的差异在于在jdk1.6中 intern 方法会把首次遇到的字符串实例复制到永久待(常量池)中,并返回此引用;但在jdk1.7中,只是会把首次遇到的字符串实例的引用添加到常量池中(没有复制),并返回此引用


-----------------------Class对象(反射中各个方法执行时期)-------------------------
同一类的不同对象它们公用同一个Class对象,静态属性显示赋值和静态代码块的执行时期就是生成Class对象完成的及,加载类到内存中
在反射中Clazz clazz = Class.forName("....")执行这里它是将类加载到内存中,这里static代码块和static属性显示赋值也是在这里执行的
clazz.newInstatnce()这里会创建对象实例(它调用无参构造器),执行到这里成员属性显示初始化会被执行,构造代码块执行,在就是默认构造方法中代码执行。。。<p align="left"></p>

-----------------什么样的类可以放入try中自动关闭----------------
实现AutoCloseable这个接口

---------------String使用+和StringBuffer使用append-------------
分析源码只String为final char [] buf;它不能改变内容如有使用+来添加数据时它是先获取原先数据再把要添加的数据一起放入到一个新的String中你使用多少个+就会创建多少个String对象
StringBuffer就不同它char[] buf,它在添加数据时会先判断容量够不够不够就扩充char[]

--------------------------------关于锁的子父类继承问题-------------------------
1. 如果父类有个同步方法(并且锁住的是this对象时)子类没有重写它,当使用这个方法时它和子类同步方法相互影响
2, 如果父类有个同步方法子类复写并还保留synchronized关键字它和子类同步方法也是相互影响
3. 如果父类有个同步方法子类复写了并没有保留synchronized方法它不会影响子类的同步方法(注意它没加synchronized也是重写,也体现了多态)


-------------------------关于java.util.concurrent下的新加类-------------------
CyclicBarrier
CyclicBarrier(int parties)
int await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
void reset()  将屏障重置为其初始状态。

CountDownLatch
CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch。
void await()  使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void countDown()  递减锁存器的计数,如果计数到达零,则释放所有等待的线程。


--------------------------深入理解动态代理----------------------
首先要清楚它也是采用注入方式实现代理
其结果public $Proxy0 extends Proxy implements 你注入进来对象所实现的接口们;
$Proxy0中的字段:
Method m0,m1,m2,m3..;
其中有些是要代理的方法 (m0 = $Proxy0.class.getDeclaredMehtod("kiss",null))
它上面主要的方法:
就是要代理对方上接口中方法
public void kiss(){
   super.h.invoke(this,m0,null);
}
源码解析:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        throws IllegalArgumentException {
        //将要代理类上的接口克隆
        final Class<?>[] intfs = interfaces.clone();
       
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);//获取动态代理Class(加载到内存)
        final Constructor<?> cons = cl.getConstructor(constructorParams);//获取动态代理上的构造方法
        final InvocationHandler ih = h;  
        return cons.newInstance(new Object[]{h});//调用上构造方法(注入要代理的方法)
        
    }

注意:这个代理类($Proxy0)它中所具有的代理方法和上面传人的第二个参数有关(相同)。
($Proxy0)kiss------------------>h.invoke------------------->真正对象方法对象.invoke
super.h.invoke(this,m0,null)     
1.你第二个参数传人的接口决定$Proxy0是否具有这个代理方法
2.而是否可以正确运行就看h方法中真实对象是否具有这个方法


---------------Class.forName,类.class,对象.getClass()的作用---------------------
它们都是加载类到内存中,如果此类已经加载到内存中。那么它直接从内存中获取(具体是方法区中)
0 0
原创粉丝点击