Java类学习与整合
数组创建:
A、 float f[][] = new float[6][6];
B、float []f[] = new float[6][6];
D、float [][]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通过set和get方法对各个时间分量进行设置及获取。
关于时间的三个类,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有重复数据时,用for从0开始检验删除,是删不掉的。因为游标跳过。
* 有两种方法,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.
key与value一一对应。可以理解为,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放的是一个一个。
并且,实现HashMap的Key的HashCode,通过HashSet
HashMap通用性更强,相当于HashSet是HashMap的特殊情况。
由Hash算法取到HashMap的Key进行计算放进储存位置。
HashSet与HashMap需要注意的问题是:
1,必须写好equals与hashCode()方法
• 同样的扩容问题,在初始化时,要确定比较好的容量
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:
2.1异常处理
异常处理内容通常有三样:
• 通知:通知用户出现了什么错误,throw
• 收拾现场:也就是catch处理
• 退出:
Throws:1
总结:
1,catch里捕捉的异常类型,必须与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{} +方法声明抛出异常。
(5)try-catch以父类异常捕捉了,处理了,但在catch里并没有被抛出,虽然在方法里声明了抛出异常,但执行到了catch块后,异常早被处理了。
Finally:
Try{
}catch(){
}
Close();
上面,顺利执行会 close,执行catch之后,也close.可是当在catch里throw时,不会close.
因此,我们需要用到finally.
Exception
RuntimeException:这个异常与其子类可以省略掉throws Exception的声明。因为这个异常,一般情况下发生了,没有什么可以做的了,默认后台打印错误信息(虚拟机默认处理方式)。
你也可以加try,catch.或throw(跟上面一样)
常见RuntimeException:
• IllegalArgumentException
• NullPointException
而一般的异常是在用户界面终止的。
注意:try{}catch{}里有return,先finally后return;
多个catch
方法重写时的异常。
子类在重写父类的方法时,父类的方法声明抛出异常。子类重写的方法也可以重写方法时声明抛出异常,但这个异常,必须是父类方法声明的异常本身或其子类。
B extends A();
A a=new B();
• write; //在编译时,是根据父类的write方法编译的,而在运行时,检测到a指向B类 b,,执行的是b的write方法
扩展:
前类对象方法调用后类方法。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筛选出来。支持回调。
扩展:牢记
1,Contains()
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足够了。
FIS,FOS
处理流,控制流任意组合加工。只需要一个基本流就行。
(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
(5)FileReader/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
3,BufferedReader/BufferedWriter只能连Reader,Writer后缀的。即1,2。
4,PrintWriter:打印流,把对象,时间,整数什么的,都变成字符串的数据输出。
PrintWrier pw=new PrintWriter(InputStream Reader);
5,System.out.println()可以认为,PrintWriter跟System.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运行时间。
多进程与多线路关系:现在的操作系统都是支持多任务的,而这个多任务通过是通过多进程实现的。而每个进程内部,又可以通过多线程实现进程内部的多线程。
线程创建的三种方式:
1,Thread类:(一个类实现了Thread类,此类就被称为多线程类)
* 1,创建线程第一种方法
* MyThread extends Thread{......}
* MyThread t=new MyThread();//不要Thread t=new MyThread();否则,MyThread用不到特别的方法
* t.start();//要正确使用线程,必须调用.start()
2.Runnable接口:如果一个类只通过继承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能解决第一种线程(单继承方式)的不能访问别的类的问题(单继承的局限性:资源不能共享),并且,本身缺点是,java在Thread类提供全面的线程操作,而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类,只是作为主方法里面的一个普通类实例;
Thread的run()可以重载,但重载后,不能作为执行线程的程序。并且,在主方法里不能通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.
注意假如A类a1,a2,a1与a2是不同的两个对象,线程访问到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 的方法对List和Map的同步
补充:单例模式:(在整个java项目中堆中只能有一个对象实例)
• 有一个private修饰的构造方法
• 有个应用类型的成员变量
线程安全API:
体系架构
任何体系架构,都是基于C/S的。客户端/服务器端
例如:手机,汽车导航仪,都是C。
发出请求的,是C,都是C访问S。
B/S是C/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只写这个默认是使用本对象守门。
多个方法都用Synchronized(this)修饰的话,默认是使用本对象守门。这样,同一时间,只能一个线程访问同一个对象守门的方法。
如果其他对象的话,可以这样写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一样,很厉害。唯一的区别是开源。同样很厉害。
DB2:IBM。很贵。
中型:
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方法,实际上是把连接放回到连接池里。
完了。
4,DAO的事务与批处理。
控制事务。
事务管理是基于线程单例的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,能真正地确定事务的原子性。
所以,当要使用到数据库两种操作以上时,尽量使用事务。。不跨事务的方法里取消自动提交。把delete和add原理实现的代码实现后,再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,无参构造函数
4,set,get方法
具体实现,编写DAO要注意的问题:
• SQL注入的问题PreparedStatement
• SQL异常如何处理
• 分页查询
• 事务的问题
• 批处理的问题
• Java和oracle的关系的问题(很重要+)面试的时候一定要说。
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所有的核心类加载进方法区。我们的类,第三方的类,会在用的时候被装进来。
反映Reflection是java提供的一组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虚拟机运行时机。
2,Java虚拟机,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,只要小数位为0或0.5,0.25,之类的,永远不会发生精度损失
DMS:
MVC,基于面向对象的一种常见的体系架构。
M | Model:业务处理,与数据库打交道,封装业务处理类 |
|
V | View:呈现视图给用户
|
|
C | Controller:控制流程,是M,C的枢纽
|
|
简单的工厂模式:流水线操作。
如 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{
}
面向对象的三种关系。
一、关联:1,B是A的组成部分(整体与部分的关系:组合(不可分割),聚集(车与发动机))
实心菱形指向,组合。空心菱形指向,聚集。
2,用一下,经常要用到的,却不是组成部分。例如,序列化的transient.
二、依赖:1,B类是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出来了,具有交互性。
未来程序员四大法宝:Java,Oracle,Html,JS.