經(jīng)典再現(xiàn) 基于JAVA平臺(tái)開(kāi)發(fā)坦克大戰(zhàn)游戲
一、需求描述
1.功能性需求
在功能需求分析階段,我們的主要任務(wù)是指定系統(tǒng)必須提供哪些服務(wù),定義軟件完成哪些功能,提供給那些人使用,功能需求是軟件開(kāi)發(fā)的一項(xiàng)基本需求,是需求分析必不可少的一部分。坦克大戰(zhàn)是一款經(jīng)典游戲了,本游戲?qū)W習(xí)了一些前輩們的經(jīng)驗(yàn),整體來(lái)說(shuō)講,游戲分為敵我雙方,主要參與戰(zhàn)斗的坦克有玩家控制,敵人坦克可以智能隨機(jī)出現(xiàn)在屏幕上,并且移動(dòng),發(fā)射一定數(shù)量的子彈;玩家可以在規(guī)定的區(qū)域內(nèi)隨意移動(dòng)坦克,當(dāng)有子彈擊中玩家時(shí),玩家死亡,游戲結(jié)束;敵人坦克智能運(yùn)行,敵方坦克由于需要具有一定智能性,隨機(jī)出現(xiàn)在屏幕上,自由的轉(zhuǎn)動(dòng)例如:碰到邊界知道轉(zhuǎn)向;子彈的運(yùn)行:坦克有玩家控制打出,根據(jù)不同的坦克位置發(fā)射子彈,如果擊中目標(biāo),則會(huì)產(chǎn)生爆炸效果;在屏幕上面也將消失。
2.系統(tǒng)性能需求
游戲?qū)τ诨谟?jì)算機(jī)系統(tǒng)的性能配置要求是保障能使程序快速,穩(wěn)定的運(yùn)行,運(yùn)行時(shí)能夠及時(shí)的響應(yīng),當(dāng)游戲中玩家坦克被擊毀,及時(shí)提示游戲失敗的信息,能及時(shí)有所回應(yīng),以滿足游戲的規(guī)則需求,另外還要保證玩游戲時(shí)候,主窗口的大小是不能夠隨意改動(dòng)的,保證有可玩性。
3.功能解決的問(wèn)題
游戲中需要代碼利用率很好,畢竟是一個(gè)實(shí)時(shí)運(yùn)行的作品,每毫秒都會(huì)有很多個(gè)子彈發(fā)射,以及很多個(gè)坦克的坐標(biāo)移動(dòng),無(wú)數(shù)次的對(duì)比子彈是否擊中坦克,對(duì)于鍵盤監(jiān)聽(tīng)事件,以及實(shí)現(xiàn)線程這個(gè)接口,繪圖的重繪刷新等問(wèn)題;邏輯感需要較強(qiáng),以及面向?qū)ο笳宫F(xiàn)的淋漓盡致;甚至之間的關(guān)系搞不清楚,很容易出現(xiàn)意外的情況;游戲中為了使加入一下美觀,增添了爆炸這一現(xiàn)象,那么這就需要當(dāng)敵人坦克死亡的時(shí)候在外面的面板上實(shí)現(xiàn)對(duì)固定地方圖片進(jìn)行短暫快速的輪播,實(shí)現(xiàn)一個(gè)爆炸效果;
總的來(lái)說(shuō)對(duì)于坦克大戰(zhàn)所要完成的基本的功能有:圖形化界面、敵我坦克樣式區(qū)別、坦克可以發(fā)射子彈攻擊對(duì)方,但卻不能攻擊隊(duì)友,坦克設(shè)置一定的生命值;
二、主要功能分析
在坦克大戰(zhàn)游戲開(kāi)發(fā)過(guò)程中,實(shí)現(xiàn)的主要的功能;提供給用戶所使用,所以首先要畫出來(lái)個(gè)坦克。
1.畫出玩家坦克: 需要在JPanel面板上面設(shè)置一張畫紙paint()并用畫筆draw出坦克的大致形狀;
2.玩家的坦克可以移動(dòng): 如果要是坦克運(yùn)動(dòng)起來(lái),需要改變坦克的坐標(biāo),并且不停的重繪面板,但什么時(shí)候讓坦克移動(dòng),可以在面板的類中實(shí)現(xiàn)事件監(jiān)聽(tīng)機(jī)制(鍵盤監(jiān)聽(tīng))的接口,當(dāng)玩家摁下w/d/s/a鍵可以對(duì)應(yīng)上下左右移動(dòng);
3.并在我的畫紙paint()上面畫出敵人坦克;由于敵人坦克與玩家坦克在同一界面,需要畫出在同一畫板上;
4.玩家的坦克可以發(fā)射子彈: 玩家要想發(fā)射子彈,要有事件源,便是當(dāng)用戶摁下J鍵,事件監(jiān)聽(tīng)當(dāng)立刻發(fā)射一發(fā)子彈,但有需要顯示在用戶的眼前看出效果,所以在面板上面paint()畫出子彈;由于子彈發(fā)射出來(lái)需要朝著發(fā)射放不停的移動(dòng),所以不進(jìn)要一直改變子彈的坐標(biāo),還要在子彈速度恰當(dāng),所以需要用線程實(shí)現(xiàn),并讓面板的不停重繪,看出效果;
5.玩家子彈可以連發(fā)并且最多發(fā)五顆: 玩家若要控制子彈的個(gè)數(shù),需要在玩家摁下J鍵時(shí)候,來(lái)得出當(dāng)前存活的子彈數(shù)量是否小于5,當(dāng)符合條件,就創(chuàng)建一顆子彈,當(dāng)玩家發(fā)射子彈大小超過(guò)五顆,則不再調(diào)用系統(tǒng)的開(kāi)火函數(shù);由于發(fā)射子彈不是只發(fā)射一顆,所以設(shè)置集合存放子彈,開(kāi)火一次產(chǎn)生一個(gè)子彈,知道子彈碰到邊界死亡即是溢出子彈集合;
6.玩家坦克擊中敵人坦克則消失,并產(chǎn)生爆炸效果;首先要判斷玩家坦克的子彈是否擊中了敵人,所以要有不停判斷擊中敵人的函數(shù),當(dāng)子彈運(yùn)行到敵人坦克上,坦克生命需要終結(jié),并產(chǎn)生一顆炸彈,并在paint()畫出炸彈;
7.敵人的坦克可以智能移動(dòng): 如果要是坦克只能移動(dòng),需要隨機(jī)產(chǎn)生移動(dòng)方向,由于不停的運(yùn)動(dòng),所以將方法寫入到線程的run函數(shù)里面,并控制線程休眠來(lái)實(shí)現(xiàn)移動(dòng)速度問(wèn)題;
8.敵人坦克以可以發(fā)射子彈: 在畫出敵人的同時(shí),在線程中設(shè)定一定休眠產(chǎn)生不同方向,不同數(shù)量坦克的子彈,并在面板上取出每顆子彈,并畫出;
9.敵人子彈擊中玩家,玩家消失: 不停的取出敵人的每顆子彈,與玩家坦克進(jìn)行比對(duì),當(dāng)子彈觸碰到玩家,玩家爆炸消失;
三、概要設(shè)計(jì)
•角色屬性設(shè)置
坦克:坦克產(chǎn)生的位置,不同類型坦克的顏色,坦克生命標(biāo)識(shí)符,坦克運(yùn)動(dòng)的速度,以及不同方向的坦克;
玩家坦克:繼承坦克基本屬性之后,在次基礎(chǔ)上實(shí)現(xiàn),玩家坦克的自由上下左右移動(dòng);以及玩家坦克擁有開(kāi)火發(fā)射子彈這種功能;
敵人坦克:繼承基本坦克屬性之后,需要實(shí)現(xiàn)敵人坦克要智能的運(yùn)動(dòng),同時(shí)也要在不同位置的坦克對(duì)其進(jìn)行發(fā)射子彈線程;
子彈:子彈要有坐標(biāo)位置,速度,生命值,以及子彈應(yīng)該可以動(dòng),以及子彈也要死亡函數(shù)方法的實(shí)現(xiàn);
炸彈:炸彈的坐標(biāo),生命,以及生命漸消失的效果方法函數(shù);
•功能屬性設(shè)置
畫紙:paint()應(yīng)該畫出來(lái)玩家坦克,敵人坦克,以及顯示出死亡坦克爆炸效果;
事件監(jiān)聽(tīng):當(dāng)用戶摁下wdsa,對(duì)應(yīng)的玩家坦克應(yīng)該進(jìn)行方向的改變,當(dāng)用戶發(fā)射j則朝發(fā)射方向有顆子彈,并且應(yīng)該一直運(yùn)行到死亡,除非擊中敵方;
擊中坦克:當(dāng)子彈擊中到敵方坦克,產(chǎn)生爆炸添加到爆炸集中,并將爆炸信息畫到面紙上;
敵人擊中玩家:取出游戲里所以的敵人,以及每個(gè)敵人的子彈與我的坦克去匹配,判斷是否擊中;
玩家擊中敵人:取出玩家的每顆子彈,與每一個(gè)敵人的坦克去匹配,判斷是否擊中;并將擊中坦克相關(guān)的判斷,放到線程run()里面進(jìn)行不停并發(fā)的判斷;
具體功能分析圖如下:
•坦克角色屬性分析圖
功能畫板分析圖
坦克Xmind整體分析圖
坦克角色屬性Xmind圖
坦克功能屬性Xmind圖
四、系統(tǒng)實(shí)現(xiàn)
•Members.java
//Members.java package mytank9; import java.util.*; class Bomb { // 定義炸彈坐標(biāo) int x, y; // 炸彈的生命 int life = 9; boolean isLive = true; public Bomb(int x, int y) { this.x = x; this.y = y; } // 減少生命值 public void lifeDown() { if(life > 0) { life--; } else{ this.isLive = false; } } } class Shot implements Runnable { int x; int y; int direct; int speed = 1; boolean isLive = true; public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } public void run() { while(true) { try{ Thread.sleep(50); }catch(Exception e){ e.printStackTrace(); } switch(direct) { case 0: y-=speed; break; case 1: x+=speed; break; case 2: y+=speed; break; case 3: x-=speed; break; } // 判斷字段是否碰到邊緣 if(x<0||x>400||y<0||y>300) { this.isLive = false; break; } } } } class Tank { // 坦克橫坐標(biāo) int x = 0; // 克縱坐標(biāo) int y = 0; // 坦克方向 // 0 表示上,1表示右,2 表示下,3 表示左 int direct = 0; // 坦克速度 int speed = 1; // 坦克顏色 int Color; boolean isLive = true; public int getColor() { return Color; } public void setColor(int color) { Color = color; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public Tank(int x, int y){ this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } } // 敵人的坦克 class EnemyTank extends Tank implements Runnable { int times = 0; // 定義一個(gè)向量,可以存放敵人的子彈 Vector<Shot>ss = new Vector<Shot>(); // 敵人添加子彈應(yīng)該在剛剛創(chuàng)建坦克和敵人的坦克子彈死亡之后 public EnemyTank(int x, int y) { super(x, y); } @Override public void run() { // TODO Auto-generated method stub while(true) { switch(this.direct) { case 0: // 說(shuō)明坦克正在向上走 for(int i = 0; i < 30; i++) { // 敵人坦克在我的范圍內(nèi)移動(dòng) if(y>0) { y-=speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case 1: for(int i = 0; i < 30; i++) { if(x<400) { x+=speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case 2: for(int i = 0; i < 30; i++) { if(y<300) { y+=speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case 3: for(int i = 0; i < 30; i++) { if(x > 0) { x-=speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; } // 判斷是否需要給坦克加入新的子彈 this.times++; if(times%2==0) { if(isLive) { if(ss.size()<5) { Shot s =null; switch(direct) { case 0: // 創(chuàng)建一顆子彈 s = new Shot(x+10, y, 0); // 把子彈加入到向量 ss.add(s); break; case 1: s = new Shot(x+30, y+10, 1); ss.add(s); break; case 2: s = new Shot(x+10, y+30, 2); ss.add(s); break; case 3: s = new Shot(x, y+10, 3); ss.add(s); break; } // 啟動(dòng)子彈線程 Thread t = new Thread(s); t.start(); } } } // 讓坦克隨機(jī)產(chǎn)生一個(gè)新的方向 this.direct = (int)(Math.random()*4); // 判斷敵人坦克是否死亡 if(this.isLive == false) { // 讓坦克死亡后,退出進(jìn)程 break; } } } } // 我的坦克 class Hero extends Tank { Vector<Shot> ss = new Vector<Shot>(); Shot s = null; public Hero(int x, int y) { super(x, y); } // 開(kāi)火 public void shotEnemy() { switch(this.direct) { case 0: // 創(chuàng)建一顆子彈 s = new Shot(x+10, y, 0); // 把子彈加入到向量 ss.add(s); break; case 1: s = new Shot(x+30, y+10, 1); ss.add(s); break; case 2: s = new Shot(x+10, y+30, 2); ss.add(s); break; case 3: s = new Shot(x, y+10, 3); ss.add(s); break; } Thread t = new Thread(s); t.start(); } // 坦克向上移動(dòng) public void moveUP() { y-=speed; } // 坦克向右移動(dòng) public void moveRight() { x+=speed; } public void moveDown() { y+=speed; } public void moveLeft() { x-=speed; } }
•MyTankGame4.java
//MyTankGame4.java /* * 功能:坦克游戲2.0 * 1: 畫出坦克 * 2:我的坦克可以上下移動(dòng) * 3: 畫出敵人坦克 * 4: 我的坦克可以發(fā)子彈 * 5:子彈可以連發(fā)(最多可以連發(fā)五顆) * 6: 當(dāng)我的坦克擊中敵人坦克時(shí)候,敵人消失(爆炸 * 『 判斷子彈是否擊中坦克;什么時(shí)候調(diào)用;』 * 爆炸:1先準(zhǔn)備三張圖;2定義Bomb類;3在擊中敵人坦克時(shí)放入炸彈Vector 4繪制 * 7: 敵人坦克在我規(guī)定范圍移動(dòng) * 8:敵人坦克也能發(fā)子彈 * 9: 當(dāng)敵人坦克擊中我的坦克,我的坦克消失 */ package mytank9; import java.awt.*; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.event.*; import java.io.File; import java.util.*; public class MyTankGame4 extends JFrame{ MyPanel mp = null; public static void main(String[] args) { // TODO Auto-generated method stub MyTankGame4 mytankgame1 = new MyTankGame4(); } public MyTankGame4(){ mp = new MyPanel(); Thread t = new Thread(mp); t.start(); this.add(mp); // 注冊(cè)監(jiān)聽(tīng) this.addKeyListener(mp); this.setSize(400, 300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } class MyPanel extends JPanel implements KeyListener,Runnable{ // 定義一個(gè)我的坦克 Hero hero = null; // 定義敵人的坦克 Vector<EnemyTank> ets = new Vector<EnemyTank>(); // 定義一個(gè)炸彈的集合 Vector<Bomb> bombs = new Vector<Bomb>(); // 敵人坦克多少 int enSize = 3; // // 定義三張圖片的圖片的切換,才能組成一顆炸彈 Image image1 = null; Image image2 = null; Image image3 = null; // 構(gòu)造 public MyPanel(){ hero = new Hero(100,100); // 敵人的坦克初始化 for(int i = 0; i <enSize; i++) { // 創(chuàng)建敵人的坦克對(duì)象 EnemyTank et = new EnemyTank((i+1)*50, 0); et.setColor(0); et.setDirect(2); // 啟動(dòng)敵人坦克 Thread t = new Thread(et); t.start(); // 給敵人坦克談價(jià)一顆子彈 Shot s = new Shot(et.x+10,et.y+30,2); et.ss.add(s); Thread t2 = new Thread(s); t2.start(); // 加入 ets.add(et); } try{ image1 = ImageIO.read(new File("bomb_1.gif")); image2 = ImageIO.read(new File("bomb_2.gif")); image3 = ImageIO.read(new File("bomb_3.gif")); }catch(Exception e){ e.printStackTrace(); } // 初始化三張圖片 // image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); // image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif")); // image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif")); } //重新paint public void paint(Graphics g){ super.paint(g); g.fillRect(0, 0, 400, 300); // 畫出自己的坦克 if(hero.isLive==true) { this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1); } // 從ss中取出每一顆子彈,并繪制 for(int i = 0; i <hero.ss.size(); i++) { // 取出子彈 Shot myShot = hero.ss.get(i); // 畫出子彈 畫出一顆子彈,怎么畫出多子彈?遍歷 if(myShot!=null&&myShot.isLive==true) { g.draw3DRect(myShot.x, myShot.y, 1, 1, false); } if(myShot.isLive==false) { // 從ss向量中刪除該子彈 hero.ss.remove(myShot); } } // 畫出炸彈 for(int i = 0; i < bombs.size();i++) { Bomb b = bombs.get(i); if(b.life > 6) { g.drawImage(image1, b.x, b.y,30,30, this); }else if(b.life>4) { g.drawImage(image2, b.x, b.y,30,30, this); }else{ g.drawImage(image3, b.x, b.y,30,30, this); } // 讓b的生命值減少 b.lifeDown(); // 如果炸彈生命值==0踢出去 if(b.life == 0) { bombs.remove(b); } } // 畫出敵人坦克 for(int i = 0 ; i < ets.size(); i++) { EnemyTank et = ets.get(i); if(et.isLive) { this.drawTank(et.getX(), et.getY(), g,et.getDirect(), 0); // 畫出敵人子彈 for(int j = 0; j < et.ss.size();j++) { Shot enemyShot = et.ss.get(j); if(enemyShot.isLive) { g.draw3DRect(enemyShot.x, enemyShot.y, 1, 1, false); }else{ // 敵人坦死亡了 et.ss.remove(enemyShot); } } } } } // 敵人子彈是否擊我 public void hitMe() { // 取出每一個(gè)敵人坦克 for(int i = 0; i < this.ets.size(); i++) { // 取出敵人坦克 EnemyTank et = ets.get(i); if(et.isLive==true) { for(int j = 0; j < et.ss.size(); j++) { // 取出子彈 Shot enemyShot = et.ss.get(j); if(enemyShot.isLive==true) { this.hitTank(enemyShot, hero); } } } } } // 我的子彈是否擊中敵人坦克 public void hitEnemyTank() { // 判斷是否擊中敵人的坦克 for(int i = 0; i < hero.ss.size(); i++) { Shot myShot = hero.ss.get(i); // 判斷子彈是否有效 if(myShot.isLive==true) { // 取出每一個(gè)坦克與它判斷 for(int j = 0; j < ets.size(); j++) { EnemyTank et = ets.get(j); if(et.isLive==true) { this.hitTank(myShot,et); } } } } } // 寫一個(gè)函數(shù)專門判斷子彈是否擊中坦克 public void hitTank(Shot s, Tank et) { switch(et.direct) { // 如果敵人坦克方向是上或者是下 case 0: case 2: if(s.x>et.x&&s.x<et.x+20&&s.y>et.y&&s.y<et.y+30) { // 擊中死亡 s.isLive = false; // 坦克死亡 et.isLive = false; // 創(chuàng)建一顆炸彈,放入Vector Bomb b = new Bomb(et.x, et.y); bombs.add(b); } case 1: case 3: if(s.x>et.x&&s.x<et.x+30&&s.y>et.y&&s.y<et.y+20) { { // 擊中死亡 s.isLive = false; // 敵人坦克死亡 et.isLive = false; Bomb b = new Bomb(et.x, et.y); bombs.add(b); } } } } // 畫出坦克 public void drawTank(int x , int y, Graphics g, int direct, int type) { // 坦克類型 switch(type) { case 0: g.setColor(Color.cyan); break; case 1: g.setColor(Color.yellow); break; } // 坦克方向 switch(direct) { // 向上 case 0: // 畫出左邊坦克 g.fill3DRect(x, y, 5, 30, false); // 畫出右邊坦克 g.fill3DRect(x+15, y, 5, 30, false); // 畫出中間矩形 g.fill3DRect(x+5, y+5, 10, 20, false); // 畫出原形 g.fillOval(x+5, y+10, 10, 10); // 畫出直線 g.drawLine(x+10, y+15, x+10, y); break; case 1: // 向右 // 畫出上面矩形 g.fill3DRect(x, y, 30, 5, false); // 畫出下面矩形 g.fill3DRect(x, y+15, 30, 5, false); // 畫出中間矩形 g.fill3DRect(x+5, y+5, 20, 10, false); // 畫出圓形 g.fillOval(x+10, y+5, 10, 10); // 畫出線 g.drawLine(x+15, y+10, x+30, y+10); break; case 2: // 畫出下邊坦克 g.fill3DRect(x, y, 5, 30, false); // 畫出右邊坦克 g.fill3DRect(x+15, y, 5, 30, false); // 畫出中間矩形 g.fill3DRect(x+5, y+5, 10, 20, false); // 畫出原形 g.fillOval(x+5, y+10, 10, 10); // 畫出直線 g.drawLine(x+10, y+15, x+10, y+30); break; case 3: // 向左邊 // 畫出上面矩形 g.fill3DRect(x, y, 30, 5, false); // 畫出下面矩形 g.fill3DRect(x, y+15, 30, 5, false); // 畫出中間矩形 g.fill3DRect(x+5, y+5, 20, 10, false); // 畫出圓形 g.fillOval(x+10, y+5, 10, 10); // 畫出線 g.drawLine(x+15, y+10, x, y+10); break; } } //對(duì)鍵摁下做出處理啊 a向左 s 向下 d向右 w向上 @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub if(e.getKeyCode()==KeyEvent.VK_W) { this.hero.setDirect(0); this.hero.moveUP(); }else if(e.getKeyCode()==KeyEvent.VK_D) { this.hero.setDirect(1); this.hero.moveRight(); }else if(e.getKeyCode()==KeyEvent.VK_S) { this.hero.setDirect(2); this.hero.moveDown(); } else if(e.getKeyCode()==KeyEvent.VK_A) { this.hero.setDirect(3); this.hero.moveLeft(); } if(e.getKeyCode()==KeyEvent.VK_J) { // 判斷玩家是否摁下J // 開(kāi)火 if(this.hero.ss.size()<=4&&this.hero.isLive==true) { this.hero.shotEnemy(); } } // 必須重新繪制Panel this.repaint(); } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } public void run(){ while(true) { try{ Thread.sleep(100); }catch(Exception e) { e.printStackTrace(); } this.hitEnemyTank(); // 函數(shù)判斷敵人的子彈是否擊中我 this.hitMe(); this.repaint(); } } }
五、測(cè)試效果
黃色為玩家,擊中玩家
敵人發(fā)射子彈
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java增強(qiáng)for循環(huán)的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇java增強(qiáng)for循環(huán)的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09springboot使用Logback把日志輸出到控制臺(tái)或輸出到文件
這篇文章給大家介紹springboot項(xiàng)目使用日志工具Logback把日志不僅輸出到控制臺(tái),也可以輸出到文件的操作方法,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-10-10spring boot基于DRUID實(shí)現(xiàn)數(shù)據(jù)源監(jiān)控過(guò)程解析
這篇文章主要介紹了spring boot基于DRUID實(shí)現(xiàn)數(shù)據(jù)源監(jiān)控過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Java并發(fā)編程之性能、擴(kuò)展性和響應(yīng)
這篇文章主要介紹了Java并發(fā)編程之性能、擴(kuò)展性和響應(yīng),重點(diǎn)在于多線程應(yīng)用程序的性能問(wèn)題,給性能和擴(kuò)展性下一個(gè)定義,然后再仔細(xì)學(xué)習(xí)一下Amdahl法則,感興趣的小伙伴們可以參考一下2016-02-02java實(shí)現(xiàn)Composite組合模式的實(shí)例代碼
這篇文章主要介紹了java實(shí)現(xiàn)Composite組合模式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01SpringBoot集成Flyway進(jìn)行數(shù)據(jù)庫(kù)版本遷移管理的步驟
這篇文章主要介紹了SpringBoot集成Flyway進(jìn)行數(shù)據(jù)庫(kù)版本遷移管理的步驟,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下2021-03-03SpringBoot中整合MyBatis-Plus的方法示例
這篇文章主要介紹了SpringBoot中整合MyBatis-Plus的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

基于SpringBoot服務(wù)端表單數(shù)據(jù)校驗(yàn)的實(shí)現(xiàn)方式