java 基础知识总结

来源:互联网 发布:linux ChrootDirectory 编辑:程序博客网 时间:2024/05/21 09:15

Java类学习与整合

数组创建:

                      A float f[][] = new float[6][6];

                      Bfloat []f[] = new float[6][6];

                      Dfloat [][]f = new float[6][6];

                             float [][]f = new float[6][];

                            float []f[]= new float[6][];

                            float f[][]= new float[6][];

 

以上六种都是创建二维数组的正确形式。

 

1,基本数值型数组没有显式初始化,则所有元素的值初始化为默认值;

而八种包装类数组没有显式初始化,则所有元素的值默认为null.

例如:   int []a=new int[10];     a[0]=0;

         Integer[]a =new Integer[10];    a[0]=null;

2,不管是基本类型还是包装类型,只声明而不初始化,全抛出NullPointerException.因为只有初始化了,才能在内存里分配空间,有指针指向。

基本类型,包装类型,不带参数的初始化。(除了数组外),没见过,可以认为,不存在单独的无参初始化。

 

 

1,八种基本类型的字节数怎么记?

这里,我是用绕口令记的。boolean btye,short char,float int ,double long.

boolean跟byte 一组,八个字节,

short,char,一组,16个字节,

float int一组,32个字节,

double long一组,64个字节。

 

 

boolean btye,short char,float int ,double long.

boolean btye,short char,float int ,double long.

 

这样,多快速背出来,十多次后就会朗朗上口,字节数记不清,但只要记得中跟它一组的字节数,这样就能记住。。。

 

 

SimpleDateFormat类

Calendar类

Date类

Calendar通过setget方法对各个时间分量进行设置及获取。

关于时间的三个类,java里主要用到的就是这三个。其中,SimpleDateFormate这个类,用来给时间格式化,String,Date类型相互转型的中介;

Date,时间类,在java中里,简单用到时间的时候,就会用到这个类。跟Calendar类相比。若java需要经常对时间进行操作,年月日增量减量的时间,还是推荐Calendar这个类。

 

包装类,

java里主要用到就是包装类,八种基本类型,对应八种包装类,并且,包装类还封装了一些对基本类型的操作方法,都是静态的

 Integer和Double常用方法

各种类型与String之间的互转:

各种类型A静态方法:A.parseA(String str);(由Sring类型转到A

String 类型的静态方法:String.valueOf(A);(由A类型转到String类型)

 

 

BigDecimal类:算到钱的地方用这个类

 

 

BigInteger类

 

 

•         Collection接口:子接口List,Set,Queue。

List具体实现类:ArrayList,LinkedList.

Set具体实现类 : HashSet,TreeSet

主要牢记方法:(增删)

add(Object obj)     -addAll(Collection c)

remove(Object obj)   removeAll(Collection c);

 

 

size();

boolean isEmpty();

clear();

 

iterator();

 

 

boolean contains(Ojbect obj);

containAll

remain(Collection c)//保留与Collection c 共有的元素,其他的删掉

removeAll(Collection c); //删掉与Collection c 共有的元素,其他的保留

 

Object [] obj=toArray();//由Collection转换成Object[]数组

<T>[]=toArray(<T> a);//由Collection转换成指定类型的数组

 

List:

(特殊的)

Object set(int index,Object obj);

Object get(int index);

 

附1:

    Integer a=127;

    Integer b=127;

    System.out.println(a==b); //返回true;

    Integer c=128;

    Integer d=128;

    System.out.println(c==d);//返回false;

 

    为什么?

          因为Integer类型对int类型的数值采取两种不同的处理:当int类型在-128~127之间时, Integer类创建两个在这数值范围内的对象时,是放在同一块内存中的。

          假如int不在上面这个范围内,对于即使相同的数值,Integer会放在不同的内存中。

 

1,Collection接口(不完全,具体详细资料,参考360云盘Collection一节)

1,Collection集合的回顾:Collection集合操作的是对象。其下所有接口,及实现类操作的同样是对象。

2,泛型的使用

3,增强for循环,只适合使用在迭代(数组,集合),注意,这个不能代替传统的for.

      在这个for里加了一些东西,超载迭代的功能,还是用传统的for

List:for循环的缺撼

  * List有重复数据时,用for0开始检验删除,是删不掉的。因为游标跳过。

     * 有两种方法,1,用for从后面删除

     *         2,用Iterator遍历删除。

     */  

Collection

     List

         ArrayList

         Vector

            Stack

         LinkedList

 

     Set

         HashSet

            LinkedHashSet

         SortedSet

            TreeSet

         EmunSet

      Queue

         Dueue

 

二,Queue队列接口

1,Queue队列:接口,跟Collection集合一样同级的接口

    1,并且LinkedList实现的Queue接口 。

         (也就是说,LinkedList即实现的Collection的子接口List,也实现了Queue接口 )因为Queue经常要插入,删除,而LinkedList在这方面效率高

 

2,Deque双端队列:Dueue是Queue接口的子接口。

可以在两头插入或删除。

    Deque<String> deque=new LinkedList<String>();

    以下为双端队列Deque接口的方法:

          deque.pollFirst();

          deque.pollLast();

          deque.offerFirst();

          deque.offerLast();

    

3,Stack栈:当Duque限制只能从一端入队和出队时,就实现的Stack栈

 

     Deque<Character>stack=new LinkedList<Character>();

以下为Stack

 

队列,双端队列,堆栈都是由LinkedList实现的,只不过只是接收的接口是由Queue(队列),Deque(双端队列),即由上面两个接口限定了其他实现的方式,从而实现对应的功能。

 

重点:(牢记)

 

两个对象比较,a==b,“==”方法调用equal()方法,对于大部分的对象来说,equals()比较的是内存地址。因为在Object对象里的equal()就是简单的返回“a==b”;

而String类重写了equals方法,比较的是字符串的内部是否相同。因此操作字符串String”==”和equa

ls是不同的。

但有时,我们需要判断两个对象的内容是否相等,而不关心内存是否相等==”调用的是equals方法。我们可以重写equals()方法,这样就可以实现,

a==b(a,b为两个对象)的内容相等比较。

 

 

 

注意: 如果两个对象equals的值 是true,则需要使它们两个hashCode值一样。如果不利用到Hash算法储存的话,可以不写,最好写上。

假如要运用hash算法储存,HashSet如果重写equals却不重写hashCode()。相等的对象却放到不同的内存地址里。会产生矛盾。

 

HashCode是很难写的。MyEclispe提供course方便生成equal(),hashCode()方法,是根据你选择的变量,选择哪几个变量相等就认为equal为true,HashCode相同。当你肯定自己不用HashSet的话,可以不写。

 

 

三,Comparable接口

 

Collections.sort();

 

Collections.sort()默认是从小到大排序的。

•          Collections.sort(list)://用这个的前提是,list里的对象元素必须实现Comparable接口。

     而Collections.sort(list),list里的元素是我们熟悉的String,Integer.它们都实现了Comparable接口。

•          Collections.sort(list,Comparator c);//实现这个临时比较接口的类,(匿名内部类)。

 

由上面可以看到:接口实现有两种途径。

• 第一种是我们常定义的。由一个类实现一个接口 ,我们创建这个类的实例,使用接口的方法。

• 第二种就是回调方法。回调方法有两种。

(1)即在方法参数里 new 一个接口,这个接口必须实现里面的接口方法(new接口作为一个匿名类使用),外部的参数传不进去。如下。

 

例如: File [] files=dir.listFiles(new FileFilter(){

             public void assert(){

                  //方法的具体实现

             {

});

由上得知,FileFilter是一个接口,在方法里接口的实现作为一个参数实现,这个接口必须实现里面的抽象方法。(匿名类)这就是回调的第一种方法。

(2)也可以一个类先实现这个接口。例如Bed实现了FileFilter接口。而这个Bed类作为回调。

     File [] files=dir.listFiles(new Ben(String name));//这样的回调,实现上方法调用只调用Ben里的接口方法,但我们可以把外面的一些参数放进Bed里,再过渡到接口里。而上面第(1)种直接由外面参数是放不进去接口实现的方法里的。

 

(3)内部匿名类要使用外面的类,也可以在外面的类前面加个final

 

 

第三天总结:

牢记1

Scanner类

 

用这个类实现后台输入取得数据。

            i

Scanner scan=new Scanner(System.in);

           int x=scan.nextInt();

           int y=scan.nextInt();

          System.out.println(“x”:x);

          System.out.println(“y”:y);

如果要想在后台实现不停地输入数据,不停地运行,则可以参照以下语句:

while(true){

           Scanner scan=new Scanner(System.in);

           int m=scan.nextInt();

           int n=scan.nextInt();

           Field field=fields.get(m*this.size+n);

           field.setType(1);

           if(field.getMineValue()==9){

              explor(minField);

              break;

           }

           System.out.println(minField);

其中,if,break在上面用作:在循环体中,当满足某一条件,执行特定语句并跳过循环体

//1,scanner.next()只读一个字符串,空格后为第二个字符串的开始,如果想要接受第二个字符串,则再需要定义,一个.next()只读取一个字符串,多余的,扔掉。

            //另一个scanner.next()

            //2,scanner.nextLine();读取的是一行,以回车键后读入一行,]

            //trim()方法只负责把字符串前后的空格去掉

 

 

 

 

 

牢记2

 

 实例化一个对象时,如Mine对象。调用里面其中一个方法getAroundField()时。

并在这个方法里再调用另一个方法getExploe()时,假如getExploe()方法里需要用到本来的Mine对象,有三种方法:

   getExploe()加一个参数,接收上层传过来的本对象

  getExploe()不用加参数,直接调用this.(因为不是静态方法时,访问方法只能能过对象,也就是说getExploe()指定了实例对象,java提供一个this,能快速调用本实例)

假如有两个类。同一个包的A类,B类,B类方法没有生成A类方法,也没有调用A类成员与实例,那怎么在B类方法里得到A类的方法或者是成员呢?

Public static String arg=能访问到我

Public static void print(){

     System.out.println(能访问到我!);

 

}

可以在A类成员声明为public static,直接通过类名调用。方法也是如此。

Static 决定是全局的,能通过类名或类实例访问,pulbic决定是可以被任务类访问。

 

 

 

Set

 

Set,是个接口。

1 元素是无序的,不能通过下标取得元素。里面的元素也不能重复

2,遍历通过Iterator或增强for循环。

 

 

HashSet

 

Set的实现类,SHashSet遍历效率快。因为其实现原理是:

   放在HashSet里的元素实现了hashCode()方法,在储存时,由hash算法计算元素的hashCode来确定储存位置。

   当在HashSet中查找某个元素时,不用像List那样遍历一遍,而是由hash算法计算这个元素的hashCode,找到对应的储存位置,判断是不是同一个对象(判断要用到equals()方法)。

   因此,要放在hashSet方法里元素假如重写了equals()方法(认为两个对象相等),而不重写hashCode()方法(却放在不同的储存位置),这会出现矛盾。因而,equals()hashCode() 两个必须很好地重写成一致。(可以利用MyEclispe提供的方便生成方法。)

如果两个对象equals的值true,则必须使它们两个hashCode值一样。反例HashSet

 

 

缺点是:HashSet扩容(一次扩一倍)问题,效率问题:数据超过装载因子,会把数据全赶出去,再重新放进来,从而保证住户比例。放入的效率低。

解决方法,空间换效率。刚开始的时候就一次过把空间给足。

Set set1=new HashSet(100);

 

      

Map:

   key-value.

   keyvalue一一对应。可以理解为,key是索引,value是值

   方法有:

     V put(K key,V value)     //

     V get(Object key)       //

     V remove(Object key);   //

     继承自上一个类的

      isEmpty();

      equals();

 

      clear();

 

     再多的查询API

 

HashMap

 

HashSet,HashMap都是由Hash算法算决定储存位置的。

 

区别是,HashMap放的是一对一对放在位置里,而hashSet放的是一个一个。

并且,实现HashMapKeyHashCode,通过HashSet

HashMap通用性更强,相当于HashSetHashMap的特殊情况。

Hash算法取到HashMapKey进行计算放进储存位置。

 

HashSetHashMap需要注意的问题是:

1,必须写好equalshashCode()方法

   同样的扩容问题,在初始化时,要确定比较好的容量

 

 

List是根据放进去的顺序,然后取得元素的。其中,ArrayList遍历的效率高,而LinkedList用在插入,删除的时候,效率高。

Set一般是用来放那些不关心顺序,然后,不会重复的元素。(HashSet,TreeSet

Map,一般是根据Key值查找Value数据的.HashMap被大量的应用(HashMap,TreeMap)

Set是放进不重复的元素。  

  

  

 

 

 

 

 

增加for循环就是为了代替Iterator.

  遍历HashMap.

  //HashMap.keySet()返回一个包含所有key的集合

  For(String key:stuMap.keySet()){

System.out.println(key);

Student student=stuMap.get(key);

}

 

 

2,Java异常处理机制

 

Throwable

Error:   包装那些Java虚拟机本身出现的错误。

Exception:

 

 

21异常处理

 

     异常处理内容通常有三样:

          通知:通知用户出现了什么错误,throw

          收拾现场:也就是catch处理

          退出:

    

Throws:1

 

 

总结:

1catch里捕捉的异常类型,必须与try里抛出的一致。当catch捕捉的异常,即使以父类异常抛出,但在调用的方法里也必须通过真实类型的异常捕捉!!!

 

2,异常处理方式:

1只抛异常:假如方法里try-catch,没有什么可收拾的,只throw e,方法声明抛出异常。也可以.直接省略try-catch不写。只需要在方法声明里直接声明抛出异常 throws Exception

 

2处理一部分,抛出一部分异常: try-catch捕获异常处理一些,捕获抛出一些,方法声明“throws Exception ”不能少(runtimeException除外,java虚拟机默认处理方式,不用声明),在catch语句里抛出。。

3直接try-catch处理异常

4)如果要抛出异常,但是又能要finally一些语句。Try{} finally{} +方法声明抛出异常。

5try-catch以父类异常捕捉了,处理了,但在catch里并没有被抛出,虽然在方法里声明了抛出异常,但执行到了catch块后,异常早被处理了。

Finally:

  Try{

  }catch(){

  }

  Close();

上面,顺利执行会 close,执行catch之后,也close.可是当在catchthrow时,不会close.

因此,我们需要用到finally.

 

 

Exception

   RuntimeException:这个异常与其子类可以省略掉throws Exception的声明。因为这个异常,一般情况下发生了,没有什么可以做的了,默认后台打印错误信息(虚拟机默认处理方式)

  你也可以加try,catch.throw(跟上面一样)

   常见RuntimeException

          IllegalArgumentException

          NullPointException

而一般的异常是在用户界面终止的。

 

注意:try{}catch{}里有return,先finallyreturn;

多个catch

 

 

方法重写时的异常。

 

子类在重写父类的方法时,父类的方法声明抛出异常。子类重写的方法也可以重写方法时声明抛出异常,但这个异常,必须是父类方法声明的异常本身或其子类。

 

B extends A();

 

A a=new B();

   write;    //在编译时,是根据父类的write方法编译的,而在运行时,检测到a指向B b,,执行的是bwrite方法

 

扩展:

前类对象方法调用后类方法。A调用B的方法,B类调用C类的方法。

C类抛出异常,而B类的中间方法直接声明抛出异常,而A类方法也可直接声明抛出异常给上层,或自己处理。

 

方法重写:要尊从两同两小一大的原则。

两同1,方法名相同,2,参数类型和顺序相同

两小1,返回值类型不大于被重定的方法的返回值类型,但不能是void(如果被重写的方法有返回值类型)

      2抛出的异常类型不大于被重写的方法抛出的异常,子类方法的异常这时可以不抛出

一大:访问限制修饰符大于等于被重写方法修饰符范围

 

 

 

 

 

 

 

 

 

File

下面的,才能真正创建目录

File file=new File("E://heee//hello22");

        File file2=new File(file,"test.txt");

        if(!file.exists()){

            file.mkdirs();

            try {

                file2.createNewFile();

            } catch (IOException e) {

                //TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

        System.out.println((file2.isFile()));

 

File.mkdir();//创建此抽象路径名指定的目录。mkdir()只能创建一层目录

File.mkdirs();//创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。mkdirs()能创建多层目录

 

File.exist();

File.mkdir();

File.mkdirs();

File.getName();

File.getPath();

File.length();

File.isFile();

File.delete();只能删除空目录

 

File[ ]=File.listFiles();  //返回本目录的子目录或子文件,只能深入一层。前提是目录。isDirectory判断是不是目录

File[]=File.listFiles(FileFilter filter);//使用File筛选接口,返回true筛选出来。支持回调。

 

扩展:牢记

 

1Contains()

Collection接口的方法Contains()有没有包括某个对象,其下接口或实现类都实现了这个方法。

2

String.endWith(.txt),判断是不是以.txt结束的。

String.indexOf(String subString),字符串内有没有包括另一段字符串。

 

 

 

时间驱动:Timer类

 

类里,private Timer timer;

timer=new Timer();

 

并在某个方法里决定执行某些操作后启动时钟:

 

Timer.schedule(task,delay,preiod);

 

Timer.schedule(new TimerTask(){

Public void run(){

Update();//本类某个方法

 

});

 

 

Java IO

 

 

      RandomAccessFile raf=new RandomAccessFile("E://hellow//abc.txt","rw");

        System.out.println(raf.getFilePointer());

       //raf.read()读的是一个字节(8位)却返回int(32)

       /*1,返回int,运算更方便

        *2,放到int的低八位,前面补零,只能为正数。这样返回-1表示读到末尾,没有冲突。

        */

       //每次只读一个字节,之后,文件指针向前移一位

       //int b=raf.read();

      

       //System.out.println(b);

       //System.out.println(raf.getFilePointer());

       //移动文件指针。

 

  牢记:

RandemAccessFile类的使用:

一次读一个字节:int raf.read();

一次读一个字节数组:raf.read(byte [] b)

//int n=raf.read(byte[] bytes);一次读,读一个b数组大小的byte数组

//如果没有数组这么多的话,有多少读多少。n返回每次读取的多少。

//如果n=-1,就已经读到末尾了。不用再读了。]

一次写一个字节:void raf.write(int b)--

一次写一个字节数组raf.write(byte[] bytes,int off,int leng);

Int n=-1;

If(n=raf.read(bytes)!=-1){

         Raf.write(bytes,0,n)

      }

 

 

 

访问文件,File,RandomAccessFile足够了。

 

 

 

 

 

 

 

 

FISFOS

 

 

处理流,控制流任意组合加工。只需要一个基本流就行。

1)文件输出输入流

InputStream

   FileInputStream

OutputStream

OutputInputStream

 

FileInputStream/FileOutputSteam记三个方法:

   Fis.read();

   Fos.write();

  Close();

其中,文件输出流时,若存在文件则覆盖,若想追加内容,使用另一个构造方法:

FileOutputStream fos=new FileOutputStream(bfile,true);

 

FIS,FOS功能不强,加管子就行。

 

(2)缓冲输出输入流:BIS/BOS默认是8K。缓冲作用。

Bis.read()

用户访问一点数据,Bis.read()调用Fis.read()取了8K,放在缓冲区里。用户再次访问未超过缓冲数据时,从缓冲里取。

Bos.write():攒满8K,就写。效率提升。

 

 

 

 

Bis/Bos注意几点:

   关电脑了,缓冲区一点数据还没写出去。

   数据不是8K的整数倍

解决:Bos.flush();

 

 

(3)数据输出输入流:DIS/DOS默认是8K。缓冲作用。

 

 

 

文件输入输入出流只能对字节读写。不能通过这两个流把那些基本类型正确输出。

 

 

 

publicstaticvoiddosTest()throws IOException{

       FileInputStream fis=new FileInputStream("E://add.txt");

       DataInputStream dis=new DataInputStream(fis);

      

       for(int i=0;i<100;i++){

          double r=dis.readDouble();

          System.out.println(r+" ");

       }

    }//

 

(4)文本文件:

InputStreamReader/ OutputStreamWriter

 

5FileReader/FileWriter:就是(悍死ISR+FIS/OSW+FOS);

只能采用系统默认的字符编码,只限于读写文件。

 

(6)BufferedReader/BufferedWriter:对读取字符进行缓冲。只套在InputStreamReader/OutputStreamWriter上面。

 

IO流体系:处理流,控制流任意组合加工。只需要一个节点流就行。

 

InputStream/OutputStream(读字节)

      FileInputStream/FileOutputStream:直接连文件夹

In read() void write(int b)读写int的低八位

      BufferedInputStream/BufferedOutputStream:对流进行缓冲

    read () write() flush()

      DataInputStream/DataOutputStream:把基本数据类型读写到流里

    readInt() readDouble() ……

    writeInt() writeDouble() ……

Reader/Writer(读字符)

      InputStreamReader/OutputStreamWriter

 int read() :读一个字符,取int的低八位,可强转char

      FileReader/FileWriter

 3BufferedReader/BufferedWriter只能连Reader,Writer后缀的。即12

 

4PrintWriter:打印流,把对象,时间,整数什么的,都变成字符串的数据输出。

   PrintWrier pw=new PrintWriter(InputStream Reader);

 

5,System.out.println()可以认为,PrintWriterSystem.out.类型差不多。

System.setOut(new PrintWriter(fos));可以把System.out.不打印到控制台,打印到文件里。//输出重定向

 

对象序列化:

ObjectInputStream/ObjectOutputStream:把对象流变换成字节流,即对象序列化。

 

序列化不能被继承   序列化就是把类,对象用ObjectInputStream/ObjectOutputStream拆成字节流。

序列化缺点的,双方都必须支持Java

并且,一个类要实现序列化,内部的东西也得实现序列化。包括类。

 

Object obj=readObject();ObjectInputStream

Void writeObject(Object obj);  ObjectOutputStream+    

 

 

1,官方提供的序列化是序列成字节流的。缺点是,双方都必须支持Java

2,但通用的是,网络传输最通用用的是字符串。即字符串在网路上传输。所以在发送端对象序列成字符串,在接收端利用这个字符串String还原成对象。

 

                                                                                                                              

Java 线程(概念必须要背

进程概念1,进程是程序的一次动态执行过程,经历代码加载,代码执行,执行完毕的整个过程,这也是进程本身从产生,发展到最终消亡过程。

2,分配固定的储存空间,每一个进程都有自己独立的进程空间。

能窜出窜去取数据的,那是黑客。

 

线程概念1,线程是进程的顺序执行流,是在进程的基础上进行划分的。跟线程一样,都是实现并发的一个基本单位。

2,每一个线程,都共享进程内的所有空间。

 

多线程机制:则是指程序可以同时运行多个程序块,使程序的运行效率变得更高,能克服传统程序语言无法解决的问题。例如,有些包含循环语句的线程可能要使用一段时间来运算,此时便可让另一个线程来做其他的处理。

 

多线程的本质:多线程的实现,是需要依靠底层操作系统支持的。同一个进程中有多个顺序执行流在执行。本质是同时持有CPU时间片的多个线程抢占CPU运行时间。

 

多进程与多线路关系:现在的操作系统都是支持多任务的,而这个多任务通过是通过多进程实现的。而每个进程内部,又可以通过多线程实现进程内部的多线程。

线程创建的三种方式:

   

1Thread:(一个类实现了Thread类,此类就被称为多线程类

            * 1,创建线程第一种方法

                   * MyThread extends Thread{......}

 * MyThread t=new MyThread();//不要Thread t=new MyThread();否则,MyThread用不到特别的方法

 * t.start();//要正确使用线程,必须调用.start()

2Runnable接口:如果一个类只通过继承Thread类来实现多线程,是必定会受到单继承局限的影响。使用Runnable接口实现多线程。目的,是解决第一种继承Thread的线程类访问不到别的类的缺点(单继承)。现在,使用Runnable实现的多线程,基本上不用了。

      /*

 *

 * 2,创建线程的第二种方法

 *A类去实现runnable接口。*

 *

 *class A implements Runnable(){

 *   void run(){

 *     //实现接口的run方法

 *   }

 *}

 *Thread thread=new Thread(new Class A()name”);‘name’为定义的线程名字,可有可没。

thread.start();

 *

 Thread就是控制一个线程,它里头有一个run方法。如果就是一个Thread,里面的run方法是空的。

*/

 

      内部线程类:

第二种方法Runnable能解决第一种线程(单继承方式)的不能访问别的类的问题(单继承的局限性:资源不能共享),并且,本身缺点是,javaThread类提供全面的线程操作,而Runnable创建的话,操作不多。所以出现第三种:内部线程类。通过接口创建线程,现在几乎不用了。

接口实现线程,目的,是解决第一种继承Thread的类访问不到别的类的缺点(单继承)。

可是,接口实现的话,可操作方法不多,功能不强大。才出现第三种。即线程现在用得比较多的是,在第一种的基础上,把线程放进去那个包含的类里面,(内部类)。

 

而在main方法里,

这样写:new RunnableTest3().new MyThread().start();

 

 

总结:

  1,一个类继承Thread类,则不适合于多个线程共享资源,而实现了Runnable接口,就可以方便地实现资源的共享。

 

线程要点:

     Void start():使线程开始执行:java虚拟机调用该线程的run方法

     Void stop():不要用,具有不安全性,会导致死锁。

     //t.start();是启动线程,一共有两个线程在执行。

        //t.run()仅仅只是调用run方法,是主线程在运行。

 

注意几点:《1其中,(2)(3)中方法解决第(1)种,实现线程的类不能访问外部类数据的缺点。

         2在主线程里(main)只创建Thread,而不写调用start()方法的话,thread类,只是作为主方法里面的一个普通类实例;

             Threadrun()可以重载,但重载后,不能作为执行线程的程序。并且,在主方法里不能通start()调用

 

执行main方法,至少有两个线程在运行,一个主线程,垃圾回收机制  线程。

 

.start():

   1,不能直接调用run()方法,而是通过start()去调用run()?

答:因为线程的运行需要本机操作系统的支持。多线程的实现,是需要依靠底层操作系统支持的。

   2,声明初始化一个线程,而不去调用start()方法,仅仅只是作为普通类处理;

   3.start()方法,同一个线程只能被调用一次,否则会抛出illegalThreadStateException“异常

 

 

一个线程五个状态:

其中三个很重要:就绪  运行 阻塞

就绪:等待CPU调度

运行:在被CPU运行的。

阻塞:无法运行下去,处于挂起状态,原因解决了,进入就绪状态,再等待

 

 

每个线程都有优先级。

 

Thread.sleep(time):谁运行到这的线程睡觉time毫秒数后(阻塞),醒来再重新回到就绪状,等待。

 

 

thread.sleep()方法:

thread.interrupt()方法:thread.interrupt()thread.sleep()方法一般是组合使用的,假如一个线程使用sleep()正在睡眠,interrupt()方法打断线程睡眠并抛出打断异常。

 

 

Try{

      Thread.sleep(1000);    //让一个线程进入休眠。

}catch( InterruptedException e){    

}

 

 

 

thread.join();//使一个线程强制执行,线程运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

 

 

 

 

thread.yield()//暂停本线程,出让位置给比它优先高的线程去执行,自己跑去排队了,不用我们关心怎么恢复,CPU会调度的。

 

 

 

 

 

 

 

 

守护线程:

 

  myThread.setDaemon(true);//设置守护线程。

在线程开始之前调用这一句。

 

 

 

线程同步(非常重要,编写也非常困难)synchronized

如果多个线程去操作同一个可变的资源,会产生线程不安全的问题。我们可以用synchronized加锁,让一堆都想要操作这个可变数据的线程排队去。这就是线程同步。但是加锁了效率会变低。

   线程同步的问题要注意两点:

        同步的范围:synchronized(this){….}”不能太大,不能太小。

        同步的对象:看门的人要选好。

 

        代码块外添加”synchronized(this){….}”,例如说,一个A类的方法里,有这些一个东西,默认是实例对象a,自己“看门”。这是针对同一个对象来说的。假如A类有实现,a1,这个代码块被一个线程访问时,自己给自己看门,那么别的线程就不能访问到a1.

注意假如Aa1,a2,a1a2是不同的两个对象,线程访问到a1这里,别的线程也能访问能a2.是没有冲突的。

        代码块外添加synchronized(Object bj){…..}”只不过找了别人来帮自己看门,原理跟1是一样的,不管是不是同一个人看门。

        方法添加synchronized.就是在1基础上,把范围放大到整个方法。原理跟1也是一样的。

这是线程同步的由来。

那么线程同步了,

什么情况下会释放锁?

         方法执行结束;

         发生异常;

         执行wait()方法。                                                                                                                                                                                                                                                                                                                                     

 

                             

同步多了,可能会产生死锁。                         

死锁:两个线程互相等待对方释放资料,一直在等待。比如说,synchronized(a)A类对象a:操作a对象时,别的线程访问不了a,这时,synchronized(b)B类对象b:操作到b对象时,别的线程也访问不了。但是a对象里要操作到b对象方法,此时仍在a对象,别的对象不能访问,同时,b对象里也要操作到a对象方法,但此时b对象,别的对象也不能访问。a在等待b,自己退出不了,b在等待a,b自己也退出不了。互相等待。

 

                                                                                                                                                                                                                                                                                                                               

 

                               

 

 

 

 

 

 

 

 

 

 

 

如何中断线程的执行?

线程的run方法是:一般使用执行体while语句,可以设置满足一定条件后加break;此时退出循环体,但本线程仍没结束。通过调用this.interrupt()方法中断线程。

 

  break;

  this.interrupte()

  this.isinterrupted()//判断本线程是否已经中断。

   Thread. interrupted()this.isinterrupted()方法功能一样,只是一个静态,一个需要实例化而已。

 

 

注意。Thread静态方法Thread. interrupted()//测试当前线程是否已经中断。

 

 

多线程访问一个数据,必须加锁。(线程同步)

如何正确创建并使用线程?

        首先,必须熟悉掌握线程创建的三种方法。

         1)普通类继承thread

         2)普通类实现runnable接口

         3)普通类继承thread类的实现,作为内部类。

 

用线程,而是直接调用run()时,会当作普通类方法调用来处理

 

线程安全API

StringBuilder ----StringBuffer

ArrayList ---     vector*

HashMap---    Hashtable*

Collections 的方法对ListMap的同步

 

 

 

 

 

补充:单例模式:(在整个java项目中堆中只能有一个对象实例)

         有一个private修饰的构造方法

         有个应用类型的成员变量

 

 

 

 

 

线程安全API

 

 

体系架构

任何体系架构,都是基于C/S的。客户端/服务器端

例如:手机,汽车导航仪,都是C

发出请求的,是C,都是C访问S

 

B/SC/S的一种,浏览器/服务器端

 

Socket:

Socket:封闭IP,端口号的类。相当于(电话)

Socket:封装了网络通信协议信息的类。

 

 

建立Socket需要IP,端口。建立了就有输出流与输入流。通过协议通信。

 

 

 

//多线程服务器。这种是危险的。

     /*危险如下:

      * 在这里,服务器线程多少是由客户端说了算。如何客户端很多,服务器就崩溃了。

      * 最好加限制:例如限制100个,线程池可以解决这问题。

      */

     //启动Server,运行在主线程的代码。职责是在等电话

     publicvoid start()throws Exception{

         while(true){

              Socket socket=ss.accept();

              Handler handler=new Handler( socket);

              handler.start();

             

         }

        

                 

     }

    

     //和一个用户进行沟通的线程,里面得有一个Socket

     class Handlerextends Thread{

        

         private Socketsocket;

        

         public Handler(Socket socket) {

              this.socket=socket;

         }

         @Override

         publicvoid run() {

              //TODO Auto-generated method stub

              super.run();

         }

     }

 

 

线程同步被充:

//如果一个方法操作A成员,只能让一个线程操作,不能让多个线程操作时run方法里调用这个方法。

把这个方法加个synchronized(同步),只能同时让一个线程访问。

//并且,当又有另一个方法,也是操作A成员,A成员只能让一个线程操作,,这个方法也被定义为synchronized

这个时候,所有操作A成员的方法同个时间只能被一个线程访问。

 

 

Synchronized只写这个默认是使用本对象守门。

多个方法都用Synchronizedthis)修饰的话,默认是使用本对象守门。这样,同一时间,只能一个线程访问同一个对象守门的方法。

如果其他对象的话,可以这样写synchronized(Object obj).这样,同一时间只能一个线程访问被obj守门的方法。

 

 

面试问题:商业应用的C/S架构。

客户端/服务器端的要点:

          Servlet端要有线程池

          Servlet端有缓冲队列

          客户端要有出错重传。

 

以上三点是解决多线程访问,但要线程同步下效率低的问题。

 

 

 

 

线程池的使用:

//多线程服务器。这种是危险的。

     /*危险如下:

      * 在这里,服务器线程多少是由客户端说了算。如何客户端很多,服务器就崩溃了。

      * 最好加限制:例如限制100个,线程池可以解决这问题。

      */

     //启动Server,运行在主线程的代码。职责是在等电话

 

 

主方法代码,一直在等待客户进来。创建对应的socket对象,并把socket对象传给一个实现runnable接口的类,并放到线程池里执行run()方法,创建线程执行。。

 

 

 

 

以上是商业应用的服务器端,采用线程池的方法。是在服务端应对客户请求不断访问资源时的解决方法:线程池。

 

 

缓冲队列

 

 

线程不停访问文件,打开,关闭文件是有开销的。

并且,线程同步会导致效率低问题。

用缓冲队列解决。

队列加锁,比文件加锁快得多。

 

有一个后台线程,从队列里取数据,往文件里面放。

 

队列要是满了。其他线程就只能等,让它阻塞,停止放数据的线程。

 

队列要是空了。停止拿数据的线程

 

 

 

 

 

 

在服务器端一定会出现缓冲队列的。

 

Queue.offer(data,timeout,unit);//返回boolean判断在timeout,时间里有没有放进去。返回false丢失数据。

当然,你可以把这个boolean当条件把放不进去的存起来。

 

 

 

 

 

Java构造方法的基本配置参数方法:

 

构造方法需要使用到的基础配置的初始化数据除了使用方法传参。还可以通过以下两种方法。

 * java中,习惯把一些基础的数据,配置到配置文件夹里。

 *  1,属性文件:首先是文本文件,扩展名是.properties,存储程序里面配置的信息,参数信息。

 *       java提供API,专门读取.properties.

 *  2,配置文件

 

  下面是java专门读取属性文件的用法:(不支持中文:Properties,要支持中文,要在MyEclispe环境下,对.properties进行添加

1)属性文件在指定磁盘目录下

 

1)属性文件MyEclipse开发环境相对目录下。

 

把属性文件放到跟类一个包里,然后,取得类加载器。。。。。

 

 

对象的序列化:

一个类要实现序列化,必须实现Serializable序列化接口,这个接口里什么也没有,只声明这个类能被序列化(一个标记)

     1,序列化不能被继承,子类要实现序列化,必须重新声明接口。

 2,并且,一个类实现序列化,里面的成员都必须确定能被序列化。如果成员类型没有被序列化,则必须被序列化。

3如何一个辅助成员不是类的必须构成部分,可以用transient声明不被序列化。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Java JDBC

 

       建立取得Connection连接的工具类BaseDAO(属性文件、静态代码块(单例))

       Connection默认事务(不跨事务),批处理。

       连接池。

       单例。

       事务管理(自动,或扩展),线程单例。

 

       

 

 

 

 

 

 

JDBC:通过标准(一系列接口),定义了访问数据库的通用API,不同的数据库厂商根据各自数据库的特点提供的对JDBC的实现。

 

JDBC,一系列接口。SUN公司编写了。(只写接口不写类,精力有限)

JDBC的实现类,都是由各种数据库厂商实现的。即JDBC驱动程序。

 

JDBC规范,只需要会用,细节不需要。那是数据库厂商要做的。

 

 

 

 

 

 

数据库:

 大型:

 Oracle:给有钱人用得。

   MySQL:Oracle收购了。同Oracle一样,很厉害。唯一的区别是开源。同样很厉害。

 

DB2IBM。很贵。

 中型:

SQL Server:微软。中型。

 

 

 

 

 

 

 

熟悉JDBC三个接口与接口的基本方法与实现就可以了。

Connection,Statement, ResultSet

 

JDBC第一部分内容:1,建立取得connection类。

 

           

 

 

标准的JDBC连接类如下:(取得connection,关闭connection

上面的方法是不正确的。Class.forName()装载数据库的驱动类。当这个类被加载时,Class.forName应当同样被执行,把驱动类加载到方法区。只需要被执行一次。因而用静态代码块把它包起来。

 

 

Static {}静态代码块:(重点!!)

静态代码块与静态方法的区别是:

        静态代码块是自动执行的。静态方法是被调用的时候才执行的。

        一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只被执行一次静态代码块常用来执行类属性的初始化。(初始化类中属性,是静态代码块常用的用途,只执行一次,是在所在类被加载时自动执行的。只要程序不关闭,(也就是服务器不关闭,静态代码块被执行的东西,会一直在内存中。)

        总结:静态代码块常用来初始化类中属性,而静态方法常用来操作类静态成员。

 

那些建立连接需要的driver,username,password.基本的配置参数,最好放在属性文件里。

并在建立取得连接的类里,用静态代码块初始化类中这些参数。

 

2 DAO里取数据的三步骤:

          BaseDAO里取得连接:Connection.(同一个connectin关闭了就不能再使用了。)

        查询: stmt=DBConn.getConnection().prepareStatement();

        取得结果集:

rs=stmt.executeQuery("select id,name,location from host");

// stmt.executeUpdate();//结果集使用不好的话,容易出问题。

 DAO设计方式之一:一个方法,一个连接,一个事务,一次操作。

算钱的地方用rs.getBigDecimal();返回BigDecimal对象

 

 

 

结果集:ResultSet

注意:

         结果集只能往下,不能往前走。

2,   拿到结果集,不是一次性就把所有数据放到结果集里,而是用一点,再从数据库里取一点。

3,如何结果集,Oracle日期类型,想拿到日期,用Rs.getDate().

       如果想得到具体日期与日期分秒,rs.getTimestamp();

   Rs.getDate(),rs.getTimestamp();日期类型的操作很简单。

 数据库有操作日期的函数to_char,to_date.java有日期类型与字符串的转型类SimpleDateFormate

 

 

 

 

if(!rs.next()){

                System.out.println("rs返回的记录集");

            }//ResultSet找不到记录,则返回空记录集。

如果,确定能找到的话,只有一条记录存在,则用if,而不是while

 

// int rows=stmt.executeUpdate("insert......");返回的整型会告诉你这个语句影响了几行。

 

 

Bo:数据以及对数据的操作

DAO:把对数据库的访问,操作封闭成类。

 

 

PreparedStatement.

 

 

 

 

 

PreparedStatement的好处:

     防止SQL注入    (预编译好的SQL确定含义了,插入奇怪的东西,识别不出。)

     效率高。(同样的sql.预编译,只编译一点,其后传的都是参数)

     批处理。

 

 

 

 

 

附:

基于商业应用的标准DAO,必须实现以下五点:

    对数据库访问的封装;

    单例

    连接池

    不跨事务(或事务管理,线程单例)

    需要用到批处理,就用批处理

实体类:类与数据库里的表一一对应。就是对数据库表里的数据的封装。

 

 

 

批处理:(攒到一定数量sql统一提交,)可控数量。

JDBC批处理:1)同一条sql语句,针对这语句,要插入许多组数据。

            

          2)多条不同的sql语句,批处理。

 

 

 

批量操作数据时,要考虑到事务的批量操作

 

 

批量操作:

 

 

Connection.addBatch();   //添加到批处理

Connection.executeBatch()//执行批处理,更新数据库,执行这个方法会自动调用cleateBatch()

 

 

执行到100条记录,批处理更新。

 

 

 

 

 

3,连接池:我们需要程序事先把连接都建立好,放到内存里,当要用的时候,直接从连接池里获得连接,不用建连接,用完放回连接池里。(不要试图动去做一个连接池出来,用别人的连接池就好。)连接池是必须只有一个。

从上面可以看到,对数据库操作完了,我们就关闭连接。但是数据库的连接是有限的。

连接用完了,不关的话,占用资源。不停连接,服务器崩溃。

每次建立连接,用完并关闭,也是耗时间的。

注意:商业用的DAO,是必须有连接池的!!!

 

 

连接池jar包:commons-dbcp.jar

           Commons-pool.jar

                     Commons-collection.jar

 

    由上面我们可以知道,每次访问数据库连接建立,关闭都是耗时间的。还不如一次过,当服务器启动的时候,创建一个单例的连接池,(确定一开始有多少条连接,限定同时连接多个条,能装多少条)。

用户访问时,拿到连接池的一条连接(Connection con=ds.getConnection)。(引用,不是复制

操作完后,用户调用close(),连接返回连接池。

一个服务器启动时,只有一个连接池。因而在调用DBUtils时,只创建BasicDataSourse(连接池)一次,(静态代码块(单例)的连接池。)

 

补充知识:

连接池相当于给平时的connection类,加了一套外套(一个新类),具有原本类的所有方法。大部分功能,方法定义引用真正的类,在一些别的方法,地方,还是原来的方法,却是不同的实现逻辑。

 所以,从连接池的方法取得的连接类。是一个包装类。不是oracle原来的类。而在这个包装类,实现connection接口。里面仍有一个connection。(理解)当调用这个新类的close方法,实际上是把连接放回到连接池里。

 

 

完了。

 

 

4DAO的事务与批处理。

控制事务。

事务管理是基于线程单例的connection.(底层可以实现各种操作的组装,而在上层则对应着一次事务操作)

 

 

 

 

JDBC,操作大体如下:

 

 con=openConnection();  //1,建立连接

 con.prepareStatament(); //2,执行sql语句

 ptmt.executeUpdate();  //3,更新或查询。这个方法默认是自动提交的。Commit()(事务

 

 

 

不跨事务:保持事务的原子性:

对数据库的操作是原子性的。

事务原理:(不能调用那些内部实现连接或自动提交的方法)

     例如,有一个Student类,一个对Student类实现操作的接口StudentDAO,与这个接口的实现类,StudentDAOImpl。在这个StudentDAOImpl方法里,有两个方法:一个是addStudent(),一个是DeleteStudent(),

     现在我要实现一个方法,把班里最低分的学生给删了,加一个学生进来。这个方法里原理就是删一个学生,添加一个学生。(跟addStudent,DeleteStudent一样)那么,能不能直接在这个方法里调用addStudent()deleteStudent()呢?

答案是:能。

第一种方法:

1,在这个replace()方法里,假如先调用deleteStudent(),addStudent()..

那么必须要满足两个条件:调用两个方法的外面不能再开一个connection.并且是要筛选满足条件if()确定要删的学生存在,要加的学生号不存在。{即加if,逻辑上去确定事务(增,删是一个整体)不会被中断。}

因为,每个方法里都取得一个连接。都是先拿到连接,再关闭。在外面套一层,是错误的。If条件要确保逻辑上的事务原子性,实际上还是delete一个提交,add一个提交。只不过是用if条件包装起来,选择合理的情况。

   可当遇上断电的情况呢?由上面知道,delete执行并提交了,add还没有完成。那么结果,是一半的事务已经提交了。(这种做法是不可取的。针对这情况,以后会有事务管理。)

  第二种方法:不跨事务。即开启动事务,在replace开一个connection.并在里面取消自动提交。当实现两个操作delete,add后,再统一提交。出错就回滚。

相比第一种方法,用事务好处有:1不需要if筛选符合的条件。2,能真正地确定事务的原子性。

      

 

 

 

 

所以,当要使用到数据库两种操作以上时,尽量使用事务。。不跨事务的方法里取消自动提交。把deleteadd原理实现的代码实现后,再commit。出错回滚rollback();就好比银行转账。

 

 

附:用第一种方法,两个方法实现同一个事务,则必须使用同一个Connection.(线程单例能解决。)

 

 

 

面试,商业应用的DAO

        对访问数据库的封装。

        连接池。

        单例

        不准跨事务,(保持事务原子性)

        该用批处理的就用批处理

   标准的javaBean

1,包名

2,序列化

3,无参构造函数

4,set.get方法。

 

 

 

Java设计模式Wrapper:例如连接池的关闭,并不是真正的关闭连接,而是把连接放回池里。

 

 

 

单例:在整个程序中,某个类只能有一个。(把成员设置为static,则变成单例)

     连接池之类的必须只有一份

 静态成员不能在构造方法里初始化。要使用静态成员初始化,一般使用静态代码块,static{ }

 

 

 

 

Static {}静态代码块:(重点!!)这就是单例。

静态代码块与静态方法的区别是:

        静态代码块是自动执行的。静态方法是被调用的时候才执行的。

        一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只被执行一次(跟静态方法一样)静态代码块常用来执行类属性的初始化。(初始化类中属性,是静态代码块常用的用途,但只能使用一次。

        总结:静态代码块常用来初始化类中属性,而静态方法常用来操作类静态成员。

 

    那些建立连接需要的driver,username,password.放在属性文件里。

    并且在建立基础connection的类里,用静态代码块初始化类中这些参数。

(服务器一启动,类加载器在加载类的时候,执行这个静态代码块,只被执行一次。以后,无论有多少个客户端访问,取数据,静态代码块只在被加载时初始化一次。)

 

 

面试,商业应用的DAO

        对访问数据库的封装。

        连接池。

        单例

        不准跨事务,(保持事务原子性)

        该用批处理的就用批处理

   标准的javaBean

1,包名

2,序列化

3,无参构造函数

4,set.get方法。

  基于商业应用的DAO的总体概述:

首先要明白,连接池,事务管理,线程单例的概念。

工厂Factory:设置为单例模式,工厂模式,每一个客户端访问都访问要工厂时,调用的是同一个工厂,也就是说,服务器里启动了,当factory,被调用时,只被创建一个,是能让每一个客户能访问的。

DBUtils:对数据库连接操作的静态工具类。里面包括封装对数据的访问,连接池,事务管理(基于线程单例).

     其中,连接池,也一个服务器,只能有一个。(每次访问数据库数据时,打开,关闭连接是有消耗的。所以,加入连接池,设置服务器启动时初始化多少个连接,并限定多少个连接,空闲多余时自动真正地关闭连接。当一次事务操作完(事务管理),同一个连接则完成了任务,随后的事务关闭,就会把连接返回给连接池,并没有真正地关闭连接,而是把线例单例里的连接记录杀掉。)

      事务管理,基于线程单例。也就是一次事务操作,只有同一个连接。在一次事务里可以实现多个对数据库存访问的方法,因为一次事务里,只有同一个连接。(对connection的线程单例)。事务执行完毕,事务关闭。而事务关闭有:返回连接给边接池,并取消在线程单例里,连接的记录。事务关闭返回的连接是先前从连接池里得到的,事实上并不会去关闭连接,而是把连接返回给连接池。只有连接池才有权限去真正关闭连接。

所以,DBUtils里的连接池时基于单例的。

 

注意:一次事务操作,只能从连接池里取得一个连接。当事务执行完关闭时,把当前线程连接的记录取消,并把连接返回给连接池。

连接,与具体业务无关。

调用工厂的getInstance()得到工厂时(事实上可以被任何客户端取得,并且都是同一个工厂,)

再调用工厂的某个方法(工厂操作的是接口。)。(在工厂的方法实现事务,所以每个方法里必须开启,提交,关闭。)事务控制交给工厂。

 

 

 

 

 

工厂模式:目的,把对象的生成细节封装起来,不需要知道里面是怎么生成的。

封闭对象的创建细节。一般工厂的方法返回值都是接口。而在方法里,返回的都是具体实现接口的类。而Spring把关系维护放到ContextApplication.xml里维护。Spring相当于一个大工厂。

工厂模式扩展:每次调用工厂模式,由上面可以知道,是每次创建一个新对象。

假如我想,不管调用工厂多少次,都调用同一个。怎么办?在工厂里把这个设置接口为成员,并创建对象。方法里直接调用。

 

 

 

       DAO实现一个功能模块大概有下面几个。

 

Class BaseDAO.(用来连接获取的接口)   1,单例连接池

 

Interface EMPDAO                    2,实现接口,接口可以有不同的实现

Interface DeptDAO

..

 

Class EmpDAOImpl extends BaseDAO

 implements EMPDAO

       class DAOFactory                     3,返回DAO对象,也把对象做成单例

 

 

 

 

 

 

 

 

锁:多个线程对数据进行变更要加锁。

 

两个线程操作单例,调用的是方法,它并不需要去改变谁。就算是同时改变的,也是改变数据库里面的数据,要数据库加锁,而不是程序。

 

 

 

 

 

 

 

 

 

分页查询(数据库):

   分页是对数据库数据分页的,需要访问数据库,所以分页方法定义在DAO接口里。

        真分页;全部数据不能一次取出来,用户想看哪,我查上来给你看。

        假分页;全部数据一次全部取出来。

 

 

 

附:rownum只能少,不能大。查询出来了,才给rownum一个号。

 

Rownum只能出去之后,才有这个值。用子查询。

 

 

附:

       存在new Exception(String str),可以输入字符串装配一个Exception,然后在catch

System.out.println(e.getMessage);输入字符串;

 

 

 

线程单例:

 

 

DAO除了对数据库的增删改查,还必须提供事务操作。

想想。增删改查需要组合操作时,这里要用到事务。

多个线程并发,往数据库插数据,有没有问题?(好比一个箱子。一大堆苹果往里放是没问题的。

答案:是没问题的。数据库支持,能保证多个操作往数据库里插数据。

缓冲队列是什么时候用呢?

又好比箱子,每一个拿一个苹果,有个动作,去收苹果。收完一大堆苹果不能直接往箱子里装。

装苹果的动作跟收苹果的,不能是同一个人。

 

但是,若是多个线程访问,操作数据库里某表的同一个数据,

 

一般写法:

ServiceDAO{

    Public void save(Service service);

    Public void delete(int id);

    Public void update(Service service);

   

    Public List<Service> findAll();

    Pubilc List<Service> findBy….();

    Public Service findById(int id)

}

 

通常DAO里每一个方法一个事务。

但假如要在同一个事务里,实现两种操作。(事务。)

前面的事务概念,要熟悉。

      前面说到,实现Connection的默认事务有两种考虑方向:

       一个方法里调用其他两种方法。(要在某些条件下能实现,不过不在同一个连接,即不同的事务。理解为失败。)

       第二种方法,把两种方法对数据库操作的原理提出来,放到一个实现connection事务开启的方法里。(可以运行)

 

现在学习事务管理:在前面第一种方法的基础上。,由前面讲到,不是同一个连接,不能使用事务。也就是说,事务管理是基于线程单例的。具体做法是:不采用Connection默认的事务,创建自己的事务(自己操作事务,灵活),并把事务控制不再是交给Connection管理,而上交给上层类控制,并且使用线程单例,ThreadLocal管理Connection

 

(事务管理,基于线程单例)线程单例:一个线程,一个单例。同一个线程要求访问多次,都是同一个线程,即(连接)。更好的控制事务。

 

       使事务更加灵活。

 

区别:使用事务管理(基于线程单例),事务更加灵活,内部代码麻烦点。

    而不使用事务管理(基于线程单例),外层操作不是很灵活,内部代码简单,由javaAPI管理。

 

 

DAO两种设计方式1),每个DAO方法是一个单独的事务;

                 2),DAO方法可以任意组合到一个事务中。(事务管理

                      把事务控制交到外面。

 

 

总结:从架构方面编写DAO要注意的问题。

  DAO用于对数据库的访问,其中的方法实现对数据表的增删改查。

  编写DAO时需要使用连接池技术。

  一般通过工厂模式获取DAO对象(DAO接口定义方法,通过具体的实现类来实现,使用DAOFactory获取DAO对象);一般情况下DAO对象都是单例的。(工厂类里定义为静态成员,接口接收实现类)

  DAO通常还需要封装并提供对事务的操作;

     一个DAO方法一个事务,一个DAO方法用一个连接。

     由调用DAO的模块来控制事务。一次调用过程一个连接。

 

面试,商业应用的DAO

        对访问数据库的封装。

        连接池。

        单例

        不准跨事务,(管理事务,要用到单例)

        该用批处理的就用批处理

 

 

标准的javaBean

1,包名

2,序列化

3,无参构造函数

4set,get方法

具体实现,编写DAO要注意的问题:

        SQL注入的问题PreparedStatement

        SQL异常如何处理

        分页查询

        事务的问题

        批处理的问题

        Javaoracle的关系的问题(很重要+面试的时候一定要说。

PL/SQL,访问Oracle最快的方式。

Java,只需要调一下PL/SQL,就能很方便的实现大批量数据库的更新,查找。

 

操作很复杂,查到的数据填到数据库表里。(什么时候用PL/SQL

坏处是:移植性很差(PL/SQL,只能适用到Oracle

大型数据库,公司很少变。

如果你让我对数据库做的事情很复杂,涉及到很多表,并且把数据填到表里,那么我就用PL/SQL,而不是JDBC去操作。

WEB页面的大量数据,应要使用储存过程。

 

 

Java反射机制:

 

JVM内存:

        栈:一个方法(方法被调用时,有对应的栈)中声明的临时变量

        堆:存放对象(成员变量也在堆里面),如果没有任何引用指向对象,那么垃圾回收机制去回收对象(java中,是不允许直接访问对象的,而是通过引用去访问对象的。)

        方法区:(生命周期最长)放的是类的信息。类放的最多的都是方法,所以叫方法区。

 

 

Public void f(){

   Foo f=new Foo();   //对象创建被引用,变量f声明在方法里,在方法被执行完,变量f被回//收,对象没有被指向了,过一会就会回收

 

}

 

       假如堆里载满对象,对象都被引用,堆没有空间可用。会抛出异常。

       主动调用System.gc();//但是java虚拟机鸟不鸟你是另外一回事。

 

过程Java虚拟机启动时,都会把JDK所有的核心类加载进方法区。我们的类,第三方的类,会在用的时候被装进来。

反映Reflectionjava提供的一组API

这组API可以直接拿到方法区的类的信息。可以new对象,调方法

通过编程的方式(Class.forName),把类的信息装入到方法区,甚至可以在方法区中直接创建类的信息

可以实现动态加载,动态调用,动态代理等功能。(运行的时候才能确定,叫动态)

 

Class是一个类,Class的对象用于封装一个类的信息。

Method是一个类,Method对象用于封装一个类中的一个方法的信息。

 

 

 

 

类加载,java虚拟机JVM

类加载

      预加载(程序一启动,加载JDK与第三方的包)

      编译加载(在运行时类在第一次用会先编译,后加载到方法区。同一进程只加载一次。常用)

      导入加载(导入包的class文件,第一次用时加载到方法区,不需要通过编译,同一进程只加载一次)

    总结:预加载是程序一启动时就被加载,而编译加载与导入加载是在要用到的时候才被加载,其中编译加载在要用到时,要先通过编译生成.class文件才被加载。

以上三种被加载时,同一个进程只加载一次。

 

系统可能在第一次使用某个类时加载该类(初始加载),也可能采用预加载载机制来加载一个类。

    初始加载:当程序主动使用某个类时,如果这个类还没有被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对类进行初始化。没有意外的话,JVM会完成这三个步骤,所以有时也把这3个步骤统称为类加载或类的初始化。

类加载是指:将类的.class文件读入内存,并为之创建一个java.lang.Class对象。(Class储存的是各个种类信的息),对于Class对象而言,类,实例上也是实例,它们都是java.lang.Class的对象。

 

类的加载由类加载器完成,类加载器由JVM提供。

 

通过使用不同的类加载器,可以从不同来源加载类的二进制数据。

 

 

Java虚拟机运行时机。

2Java虚拟机,JVM运行机制,当我们调用java命令运行某个java程序时,该命令将会启动一个java虚拟机进程,不该java程序有多么复杂,该程序启动了多少个线程,它们都处于java虚拟机进程里。

   前面所学的多线程,主线程main都是在这个JVM进程里执行的。

 

         

当系统出现以下几种情况,JVM进程将会被结束

       1)程序运行最后正常结束;

       2)程序运行到System.exit()或者使用Runtime.getRuntime.ext()代码结束程序

        (3)程序遇到未捕获异常或error结束

        4)程序所在的平台强制杀死JVM进程

JVM进程被结束了,那么该进程在内存中的状态将会丢失。

 

 

 

.3

 

 

 

 

 

 

 

 

 

 

 

注意:

   

A类的静态成员a:同一个类的所有实例的静态成员共享同一块内存区(方法区)。这是没错的。

问题是:

当一个jVM进程死掉了,即程序结束,内存也就被回收。而JVM内存包括栈,堆,方法区。而一个类包括静态成员,那么这个类被加载的话,静态成员,会放到方法区。程序结束了,JVM进程被结束,内存都没有了,里面那么方法区,栈,堆都没了。再次启动时,是一个新的进程,新开辟方法区。

两次运行java程序处于两个不同的JVM进程中,两个JVM之间不会共享数据。

所以,两次启动程序时,输出的结果是一样的。

 

假如只有一个程序在运行而没有结束,那么调用该类是不会再次执行类加载的,静态成员也就共享了。

类加载时机:

 

1.(预加载)java虚拟机启动时(也就是我们点击运行java时),第一次用到这个类,会通过类加载器把个类加载到方法区(这是类加载的普通方法,类加载的另一种是在系统启动时,启动预加载机制,把运行java所需要的核心类加载到方法区)

2,(编译加载)编程方式,当创建某个对象,调用某个对象再会加载到方法区

3,(导入加载的一种)能过Class.forName()把一个现成的类加载到方法区,暂时没有使用到

 

 

通过反射,不用以前的方式,加载类,以指定构造方法创建类对象,调用对象的某个方法。(这个必须会!!!)

 

 

 

扩展:

1

   Switch:

     使用switch有一个缺点,就是找到对应的条件入口,执行,执行完后,假如没有break,则继续执行下一条最近的条件入口,一直这样下去,直至遇到break或退出switch.

     假如default放在前面,也不会先被执行,而是要经过switch表达式判断。

 

 

 

 

栈;

 

方法区:方法区里,不同的类也是分配不同的内存块的。所以,不同类具有相同的静态成员名,是不冲突的。并且,属性,是不存在重写的。

如果一个子类继承父类,具有与父类相同的静态方法,与静态成员。

通过类名调用静态成员,或者子类指向父类变量,这时输出的是父类的静态成员,而调用静态方法会报错。

 

 

 

扩展:2.6在计算机内部是无法转换成准确二进制数的。

     小数位是这样算的。不断乘以2,第一次乘,得到1,写1,并减1,再乘以2,不断下去,直接减后为零。

     对于double,只要小数位为00.5,0.25,之类的,永远不会发生精度损失

DMS:

 

MVC,基于面向对象的一种常见的体系架构。

M

Model:业务处理,与数据库打交道,封装业务处理类

 

V

View:呈现视图给用户

 

 

C

Controller:控制流程,是MC的枢纽

 

 

 

 

简单的工厂模式:流水线操作。

 

class Student{

   Private String name;

}

我们构造很多学生。

String [] Students={“李四”,“张三”,“王五”}

 

可以用for循环:

For(Student name:Students){

    Student student=new Student(name);

}假如有很多条件的话,用for循环,不是很方便。

这时,我们使用工厂模式。在Student类里定义一个静态方法,批量生成student数组。

 

 

DMS V4.0

 

 

Class A{

   Private B b;

}

Class B{

}

面向对象的三种关系。

一、关联:1BA的组成部分(整体与部分的关系:组合(不可分割),聚集(车与发动机))

            实心菱形指向,组合。空心菱形指向,聚集。

         2,用一下,经常要用到的,却不是组成部分。例如,序列化的transient.

二、依赖1B类是A类方法的参数,或者B类对象是A类的返回值。(B类对象跟A类方法有关。)指向是虚线。

 

 

三、继承:继承用得越少越好。(耦合度最高

 

 

 

Java性能三大瓶颈::IO,网络,数据库

 

 

 

 

注意:如果方法都有B类成员(依赖),则可以变成关联,把B类对象定义为成员(关联)能用依赖的,尽量不要用关联。

 

 

什么是多态?同一个名称,有多种形态。

多态包括:

1,方法的多态。方法重写和方法重载。

2,对象的多态。多态的前提条件是必须发生重载。

父类的变量可以指向子类的对象。(引用类型的多态)

 

Person person=new Student();

编译时按照父类的方法定义去编译,而运行时,会按照子类重写的方法去执行。(动态绑定)。

 

多态与类型转换:

    向下转型

    向上转型

 

 

构造方法需要使用到的基础配置的初始化数据除了使用方法传参。还可以通过以下两种方法。

 * java中,习惯把一些基础的数据,配置到配置文件夹里。

 *  1,属性文件:首先是文本文件,扩展名是.properties,存储程序里面配置的信息,参数信息。

 *       java提供API,专门读取.properties.

 *  2,配置文件

 

  下面是java专门读取属性文件的用法:(不支持中文:Properties

 

 

把属性文件放到跟类一个包里,然后,取得类加载器。。。。。

 

DMS5.0

 

所以和JAVA相关的系统都是C/S架构:客户端,服务器。这个必须熟悉.

 

 

一、实际应用中的服务器必须实现:

•         多线程

•         线程池

•         缓冲队列

•         短连接

 

二、网络互相交互,底层数据实现都用字符串,能过字符流。(因为任何语言都能识别。)

 

互联网历史发展:

98年出现浏览器。浏览器逐步取代了个性化客户端。

HTTP协议逐步取代了个性化协议。

08年各大厂商推出应用服务器软件(封装,Socket,多线程什么的/),也就是服务器。

 

Ibm websphere

Bea weblogic(dea收购了。跟sun,mysql一样)

Oracle oracle 9i(数据库+应用服务器)oracle8之前只是数据库。

开源:

Tomcat(apake)

Jboss

 

以后我们就不用写多线程,Socket之类的。

 

 

Socket,浏览器帮你写了,tomcat帮你收。

 

 

 

 

 

 

 

 

旧:C/S架构桌面应用程序,客户端/服务器

新:B/S架构浏览器/服务器

 

但是B/S相对于C/S而言,交互性差,所以出现了JavaScript.

 

现在的大量JavaScript脚本运行,占内存,会经常出现打开IE就死机了。

到目前为止,HTML5出来了,具有交互性。

 

未来程序员四大法宝:JavaOracleHtmlJS.

0 0
原创粉丝点击