文章目录
堆与栈
- 声明对象创建的对象存放在堆中,而引用变量存放在栈中
- 栈中的引用变量连接这堆中的对象
- 在栈中的方法被调用时会提到最上面。
异常
- RuntimeException类型的异常不需要放到try/catch的块中
- 无论如何finally的都会执行
- 多个catch要以从小到大的顺序
- 异常最好在main中都要抓而不是抛向虚拟机
- catch与finally不能没有try;try与catch之间不能有程序;try一定要有catch或finally;如果只有try与finally就必须抛出异常
编译
- 先javac 编译成class再java
- 若要给args添加参数就用java 类名 参数
GUI
- JFrame是个代表屏幕上window的对象。
- 简单的一个程序
- 实现事件就得监听事件,就得去实现接口
- 自己绘图要用Graphics类,GradientPaint实现渐层颜色
- (放组件的一个框)三大首席管理器 borderLayout(从左到右)、blowLayout(垂直加)、boxLayout
- 框架默认BoxLayout布局,面板默认FlowLayout布局
- 重要对象JFrame、JButton、MidiEvent、Sequencer、Track、ShortMessage、JScrollPane、JTextArea、JCheckBox、JList
- 重要接口ControllerEvebtListener、ActionListener(用不起了)
JFrame frame=new JFrame();
JButton button=new JButton("click me");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
内部类
- 可以实现某一个接口的方法多次(在每个内部类中都可实现一次接口的方法)
- 无法像和独立类那样可以重用。
- 可以实现外部类和内部类通过不同的IS-A测试。
序列化和文件的输入输出
- 先将对象序列化才能传到文件中保存,序列化的对象保存了实例变量的值,就是存储了对象的状态。
- 当含有其他的引用的对象的对象被序列化时,所有被引用的对象也会被序列化(自动进行的)
- 实现Serializable的类可以被序列化,而且它的子类也可以被序列化(必须实现Serializable否则会报错)
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Box implements Serializable{
private int height;
private int width;
public void setHeight(int height){
this.height=height;
}
public void setWidth(int width){
this.width=width;
}
public static void main(String[] args){
Box box=new Box();
box.setHeight(20);
box.setWidth(40);
ObjectOutputStream os=null;
FileOutputStream fs=null;
try{
fs = new FileOutputStream("./testfile/foo.ser");//是以demo6(IO)为当前路径
os=new ObjectOutputStream(fs);
os.writeObject(box);
}catch(FileNotFoundException ex){
ex.printStackTrace();
}catch( IOException ex){
ex.printStackTrace();
}finally{
try{
os.close();
fs.close();
}catch(IOException ex){
ex.printStackTrace();
}
}
}
}
- 序列化肯定是全部被序列化或者全部都没被序列化,不可能部分被序列化,简而言之就是要么正确要么全部失败
- 如果某实例变量不能或不应该被序列化,要把他标记成transient,返回时这个变量的值为null
- 解序列化时会生成一个全新的对象,构造函数不会执行。
- 读取对象的顺序必须与写入的顺序相同
- 静态变量不会被序列化
- 读取一个文件,链接后就可以读取里面的内容,每次重新读取(链接)这个文件就会就会重新开始。
- 序列化后的源对象不要乱改否则,会导致反序列化失败,因为序列化时给源对象加了serialVersionUID(根据类的结构信息生成的)修改后id会变就是反编译报错
- 修改(瞬态变量是加了transient)
损害序列化的修改:
删除实例变量
改变实例变量的值
将非瞬间的实例变量修改为瞬时的
修改类的继承层次
将类从可序列化改成不可序列化
晶实例变量改成静态
不会损害的修改
加入新的实例变量
在继承层次中加入新的类
在继承层次中删除类
不会影响解序列化程序设定变量值得存取层次修改
将实例变量从瞬时改成非瞬时(会使用默认值)
//可序列化对象
import java.io.Serializable;
public class GameCharater implements Serializable{
private String name;
private String type;
private transient String coloth;
public GameCharater(){
}
public GameCharater(String name,String type,String coloth){
this.coloth=coloth;
this.name=name;
this.type=type;
}
public String getName(){
return name;
}
public String getType(){
return type;
}
public String getColoth(){
return coloth;
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
//序列化对象的序列和解xulie
public class GameSave1 {
private static int num=0;
public static void main(String[] args){
GameCharater gc1=new GameCharater("张三", "战士", "black");
GameCharater gc2=new GameCharater("李四", "射手", "red");
GameCharater gc3=new GameCharater("王", "法师", "blue");
GameSave1 gs=new GameSave1();
ArrayList<GameCharater> glist=new ArrayList<GameCharater>();
glist.add(gc1);
glist.add(gc2);
glist.add(gc3);
gs.outPutStream("./testfile/game.ser", glist);
gc1=null;
gc2=null;
gc3=null;//设为null就无法取到堆上的对象了
gs.inputStream("./testfile/game.ser");
}
public void inputStream(String path){
ObjectInputStream os=null;
try{
os=new ObjectInputStream(new FileInputStream(path));
for(int i=0;i<GameSave1.num;i++){
GameCharater gc=(GameCharater)os.readObject();
System.out.println("姓名"+gc.getName());
System.out.println("颜色"+gc.getColoth());
}
}catch(Exception ex){
ex.printStackTrace();
}finally{
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void outPutStream(String path,ArrayList<GameCharater> gl){
try{
ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream(path));
for(GameCharater g:gl){
num++;
os.writeObject(g);
}
os.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
IO流
- 序列化对象
objectOutputStream.writeObject(对象)
- 写字符串
FileWriter writer=new FileWriter(路径);
fileWriter.write("字符串");
- 获取目录下的文件和创建目录
import java.io.File;
import java.io.IOException;
public class MyTest {
public static void main(String[] args){
try{
File f=new File("./testfile");
if(f.isDirectory()){
File f1=new File("./testfile/chapter");
f1.mkdir();
System.out.println(f.getAbsolutePath());
String[] content=f.list();
for(String item:content){
System.out.println(item);
}
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}
- 运用缓冲区时用**对象.flush()**就是把缓冲区的内容马上写进去,
File f=new File("./testfile");
BufferedWriter bw=new BufferedWriter(new FileWriter(f));//文件套在文件的传输方向套在缓冲区
网络与线程
- socket是两台机器之间连接的对象,需要ip与端口号
- 端口号0-1023给了特定的服务器,不可用,1024-65535可用
- BindException是端口号被占用的报错。
- 尽量用PrintWriter对象去写
- socket代表两个应用程序之间的连接
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
//客户端
public class DailyAdviceClient {
public DailyAdviceClient(){
}
public void go(){
BufferedReader reader=null;
Socket s=null;
String str=null;
try{
s=new Socket("127.0.0.1",4242);
// InputStreamReader streamReader=new InputStreamReader(s.getInputStream();
InputStreamReader is=new InputStreamReader(s.getInputStream());
// FileReader fd=new FileReader(new File("./testfile/test.txt"));
reader=new BufferedReader(is);
// MyBase.MyPrint(s.isConnected());
while((str=reader.readLine())!=null){
System.out.println(str);
}
}catch(IOException ex){
ex.printStackTrace();
}finally{
MyIoTool.myClose(reader);
}
}
public static void main(String[] args){
System.out.println("客户开始");
DailyAdviceClient dac=new DailyAdviceClient();
dac.go();
}
}
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
//服务器
public class DailyAdviceServer {
String[] adviceList={"111111","2222222","3333333333","4444444444","55555555555","6666666666","7777777",
"888888888","99999999999999","1000000000000"};
public void go(){
PrintWriter pWriter=null;
String info=null;
ServerSocket serverSocket=null;
try{
serverSocket=new ServerSocket(4242);
while(true){
Socket socket=serverSocket.accept();
pWriter=new PrintWriter(socket.getOutputStream());
info=getAdvice();
System.out.println(info);
pWriter.println(info);
pWriter.close();//这儿不能把socket关闭了,否则传输失败.
//第二种方式
// BufferedWriter bos=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// bos.write(getAdvice());
// bos.flush();
// bos.close();
}
}catch(IOException ex){
ex.printStackTrace();
}finally{
}
}
public String getAdvice(){
String advice=adviceList[(int)(Math.random()*adviceList.length)];
return advice;
}
public static void main(String[] args){
DailyAdviceServer dServer=new DailyAdviceServer();
System.out.println("开始");
dServer.go();
}
}
- 线程可以实现Runable接口重写run()再传入Thread中,Runable相当于工作,Thread相当于工人;也可以直接继承Thread;(推荐第一种)
public class Myrunnable implements Runnable {
private int i=0;//可以共用对象的资源
public void run(){
i++;
go();
}
public void go(){
i++;
goMore();
}
public void goMore(){
MyBase.MyPrint("i am "+i+" "+Thread.currentThread().getName());
}
public static void main(String[] args){
Myrunnable myR=new Myrunnable();
Thread th=new Thread(myR);
Thread th1=new Thread(myR);
th.start();
th1.start();
MyBase.MyPrint("我是"+Thread.currentThread().getName());
}
}
- 线程有可执行,执行中和堵塞状态
- 线程调度器的调度,是不可控制的,只能用sleep(time)让某线程沉睡,time单位是毫秒(1000毫秒=1秒),
- run()执行线程后就不能重新启动了
- 每个线程都有独立的执行空间
- 出现两个线程操作同一个变量时会判断失效,原本第一个线程操作后就会变false(一个线程就是判断为true后睡觉,没有对数据操作,则另一个线程继续去判断也会true,然后两个就会同时去操作这个变量,对程序造成影响)(如银行取钱的账户规定不能少于10可是会造成它少于0)
package test;
//账户
public class BankAcount {
private int balance=100;
public int getBance(){
return balance;
}
public void withdrow(int amount){
balance=balance-amount;
}
}
package test;
//操作
public class RyAndMoJob implements Runnable {
BankAcount ba=new BankAcount();
public void run(){
for(int i=0;i<10;i++){
makeWrithdrow(7);
if(ba.getBance()<0){
System.out.println("Overdraw");
}
}
}
private void makeWrithdrow(int acount){
if(ba.getBance()>acount){
System.out.println(Thread.currentThread().getName()+"钱够");
try{
System.out.println(Thread.currentThread().getName()+"开始睡觉");
Thread.sleep(1000);
}catch(InterruptedException iex){
iex.printStackTrace();
}
System.out.println("取出");
ba.withdrow(acount);
System.out.println(Thread.currentThread().getName()+"取出成功");
System.out.println("还有"+ba.getBance());
}else{
System.out.println("sorry your money is not enough for"+Thread.currentThread().getName());
}
}
public static void main(String[] args){
RyAndMoJob ryAndMoJob=new RyAndMoJob();
Thread t1=new Thread(ryAndMoJob);
Thread t2=new Thread(ryAndMoJob);
t1.setName("Ry");
t2.setName("Mo");
t1.start();
t2.start();
}
}
//还有-5
//Overdraw
//sorry your money is not enough forRy
//Overdraw
//sorry your money is not enough forRy
//Overdraw
- 解决11出现的问题我们可以给这个处理的事件加一把锁来处理(小明操作的时候会拿到这把锁,直到处理完才把锁放回去,那么这样其他人就不可能在小明处理这个事件的时候同时处理这个事件)
- 在处理事件的方法上加一个synchronized表示每次只能一个线程(人)处
- 丢失更新,(加synchronized同步就能解决),就是赋值时用了两个操作,用睡眠(停留时间相差太大了)把他们这个原子操作分割了,造成了丢失更新
package syn;
public class TestSyn implements Runnable{
private int count;
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
increase();
System.out.println(Thread.currentThread().getName()+"的数字是"+count);
}
}
//方法加入synchronized就可以解决更新丢失
public void increase(){
int i=count;
if(Thread.currentThread().getName().equals("One")){
try{
Thread.sleep(1000);
}catch(InterruptedException iex){
iex.printStackTrace();
}
}
if(Thread.currentThread().getName().equals("Two")){
try{
Thread.sleep(3000);
}catch(InterruptedException iex){
iex.printStackTrace();
}
}
count=i+1;
}
}
package syn;
public class TestSynTest{
public static void main(String[] args){
TestSyn ts=new TestSyn();
Thread t1=new Thread(ts);
Thread t2=new Thread(ts);
t1.setName("One");
t2.setName("Two");
t1.start();
t2.start();
}
}
//结果
// One的数字是1
// One的数字是2
// Two的数字是1
// One的数字是3
// One的数字是4
// One的数字是5
// Two的数字是2
// One的数字是6
// One的数字是7
// One的数字是8
// Two的数字是3
// One的数字是9
// One的数字是10
// Two的数字是4
// Two的数字是5
// Two的数字是6
// Two的数字是7
// Two的数字是8
// Two的数字是9
// Two的数字是10
- 用对象锁(方法上加锁(this)),类锁(静态方法加锁(this.class))和锁块锁(唯一的对象)几个语句
- 同步化会提高消耗,也可能会导致死锁,只能最少量的使用同步。
- 同步使用不好也会导致线程死锁,伤害程序,(两个线程互相持有对方正在等待的东西,,比如锁),条件:互斥(一个资源只能给一个线程),不可剥夺(资源只能申请,不能强),请求保持(线程申请资源时,保持对原有资源的占有)循环等待。
- 死锁的列子(获取不到锁)
package sisuo;
public class SiSuoTest {
public static String lock="lock1";
public static String lock2="lock2";
public static void main(String[] args){
Lock lk=new Lock();
Thread t1=new Thread(lk);
Thread t2=new Thread(lk);
t1.setName("Demo1");
t2.setName("Demo2");
t1.start();
t2.start();
}
}
class Lock implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
String name=Thread.currentThread().getName();
if(name=="Demo1"){
synchronized(SiSuoTest.lock){
System.out.println("进入"+name+"的lock1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(SiSuoTest.lock2){
System.out.println("进入"+name+"的lock2");
}
}
}else if(name=="Demo2"){
synchronized(SiSuoTest.lock2){
System.out.println("进入"+name+"的lock1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(SiSuoTest.lock){
System.out.println("进入"+name+"的lock2");
}
}
}
}
}
简单的聊天服务器代码(接收消息,转发给别人)综合socket和Thread
package Project1;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
public class VerySimpleChatServer {
//保存用户
ArrayList clientOutputStreams;
public static void main(String[] args){
}
//将收到的消息发送给所有客户端
public void tellEveryone(String message){
Iterator it = clientOutputStreams.iterator();
PrintWriter writer=null;
while(it.hasNext()){
try{
writer = (PrintWriter) it.next();
writer.println(message);
writer.flush();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
public void go(){
clientOutputStreams = new ArrayList();
try{
//4343端口
ServerSocket serverSocket=new ServerSocket(4343);
while(true){
Socket clientSocket = serverSocket.accept();
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
clientOutputStreams.add(writer);
Thread t=new Thread(new ClientHandler(clientSocket));
t.start();
System.out.println("go a connection");
}
}catch(Exception ex){
ex.printStackTrace();
}
}
//每一个用户就一个线程
public class ClientHandler implements Runnable{
BufferedReader reader;
Socket socket;
public ClientHandler(Socket clientSocket){
try{
socket = clientSocket;
InputStreamReader isReader = new InputStreamReader(socket.getInputStream());
reader = new BufferedReader(isReader);
}catch(Exception ex){
ex.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
String message;
try{
//显示在控制台并告诉所有客户
while((message = reader.readLine())!=null){
System.out.println("read " + message);
tellEveryone(message);
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
- 补充:如果想要继续交互就用这两个关闭而不能直接socket.close这样就不能交互了
socket.shutdownInput();
socket.shutdownOutput();
数据结构
TreeSet: 有序防重复
HashMap: 成对的name/value来保存与取出
LinkedList: 插入或删除中间元素所设计的高效率集合
HashSet: 防止重复的集合,能快速寻找相符的元素
LinkedHashMap: 类似HashMap,但可以记住元素插入的顺序,也可以设定成依照元素上次存取的先后来排序。
ArrayList: 插入对象直接排在最后面。
排序
只对字符排序
List<String> list = new ArrayList<>();
Collections.sort(list)
对类中数据规定排序
//第一种(一参数)
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
//第二种(二参数)
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
第一种
传入的类必须是Comparable,所以要实现Comparable
实现Comparable
//实现了Comparable
public class SongList implements Comparable{
private String title;
private String artist;
private String price;
public SongList(){
}
public SongList(String title,String artist,String price){
this.artist=artist;
this.price=price;
this.title=title;
}
public String getTitle(){
return title;
}
public String getArtist(){
return artist;
}
public String getPrice(){
return price;
}
//重写排序规则,与返回值得正负有关
@Override
public int compareTo(Object o) {
return -title.compareTo(((SongList)o).getTitle());
// TODO Auto-generated method stub
}
}
第二种
创建一个类再重新写方法
class ArtistCompare implements Comparator<SongList>{
//重写规则,与返回的规则有关。
@Override
public int compare(SongList o1, SongList o2) {
// TODO Auto-generated method stub
return -o1.getArtist().compareTo(o2.getArtist());
}
}
去重(Set)
HashSet
HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能,可以很好的去重,但是不会安顺序排。底层数据结构是哈希表。
LinkHashSet
HashSet还有一个子类LinkedList、使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的,遍历集合LinkedHashSet集合里的元素时,集合将会按元素的添加顺序来访问集合里的元素,而且因为是链表所以插入和删除效率比较高。
LinkedHashSet依然是Set,因此它不允许集合重复。
TreeSet
TreeSet可以确保集合元素处于排序状态.
TreeSet内部实现的是红黑树,默认整形排序为从小到大
部署
注意
java源文件和编译的class文件要分开,好管理
所以就要创建source 和 classes文件
javac命令
(在source目录下的命令)自动生成classes下的包目录
javac -d classes目录 java源文件目录
javac -d 命令如果要自动生成包的目录那么就必须在源文件中添加package的路径才行
java命令
执行程序命令
java 主类名字 (先要编译成class文件才可以运行)
jar命令
jar -cvmf manifest.txt app.jar com 后面必须加com因为要指定从com目录开始找才行
manifest.txt内容:
Main-Class: com.wj.MyApp
manifest.txt 如果实在*.class之中那么就不用加com 里面的值只要是main类的命就行
jar -tf app.jar 列出文件列表
META-INF是jar包的信息文件
jar -xf app.jar解压成META-INF文件目录
执行jar包命令
java -jar jar包名
部署方式
完全部署在本机(Executable Jar)
直接打包成jar,执行
介于两者之间 (Web Start和Rmi App)
java web start
这个需要安装java和helper app。
客户端点击JWS的应用程序链接(.Jnlp文件),下载jar文件,然后就可以运行了。
.jnlp:描述应用程序课执行jar文件的XML文件
RMI
RMI(远程方法调用)
需要服务器,客户端,服务器辅助设施,客户端辅助设施
需要客户端 访问 客户端辅助设施 访问服务器辅助设施 访问服务器
关键包:rmi
关键类:Remote、UnicastRemoteObject
关键:stub和skeleton,rmiregistry
完全在远程
Servlet可以配合Jini(用来查询服务器的服务的)