Java飞机大战源码解读与二次开发教程(含代码重构技巧与新功能扩展实现方法)
Java典型模块与项目实战大全》是一座富矿,但我们需要用新的工具去开采。它的价值不在于提供可复用的代码,而在于提供了一个个完整的“问题域”和经典的解决方案蓝图。我们的任务,就是站在这个巨人的肩膀上,运用Spring Boot、微服务、Docker、Kubernetes以及AI等现代利器和思维,对这些蓝图进行“现代化改装”。这个过程,才是真正意义上的“实战演练”,它能让你不仅知其然(如何做),更知其
Java飞机大战游戏源码深度解析与二次开发实战
引言
飞机大战作为经典的游戏类型,是学习Java游戏开发的绝佳案例。本文将深入解析一个完整的飞机大战游戏源码,并详细介绍代码重构技巧和新功能扩展方法。通过本文,您将掌握从基础游戏框架搭建到高级功能实现的完整开发流程。
1. 游戏框架设计与核心类解析
1.1 主程序入口与游戏窗口
```java
public class PlaneWar extends JFrame {
private static final int SCREEN_WIDTH = 480;
private static final int SCREEN_HEIGHT = 850;
public PlaneWar() {setTitle("Java飞机大战 v2.0");
setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
GamePanel panel = new GamePanel();
add(panel);
addKeyListener(new GameKeyListener(panel));
setVisible(true);
new Thread(panel).start(); // 启动游戏线程
}
public static void main(String[] args) {
SwingUtilities.invokeLater(PlaneWar::new);
}
}
```
1.2 游戏主面板与双缓冲技术
```java
public class GamePanel extends JPanel implements Runnable {
private BufferedImage buffer;
private Graphics2D graphics;
private PlayerPlane player;
private List enemies;
private List bullets;
private boolean gameRunning = true;
private int score = 0;
private int gameLevel = 1;
public GamePanel() {setPreferredSize(new Dimension(PlaneWar.SCREEN_WIDTH, PlaneWar.SCREEN_HEIGHT));
buffer = new BufferedImage(PlaneWar.SCREEN_WIDTH, PlaneWar.SCREEN_HEIGHT,
BufferedImage.TYPE_INT_RGB);
graphics = buffer.createGraphics();
initializeGame();
}
private void initializeGame() {
player = new PlayerPlane(PlaneWar.SCREEN_WIDTH / 2,
PlaneWar.SCREEN_HEIGHT - 100);
enemies = Collections.synchronizedList(new ArrayList<>());
bullets = Collections.synchronizedList(new ArrayList<>());
spawnEnemies(); // 初始化敌人
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// 双缓冲绘制
renderGame();
g.drawImage(buffer, 0, 0, null);
}
private void renderGame() {
// 绘制背景
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, getWidth(), getHeight());
// 绘制游戏对象
player.draw(graphics);
synchronized (enemies) {
enemies.forEach(enemy -> enemy.draw(graphics));
}
synchronized (bullets) {
bullets.forEach(bullet -> bullet.draw(graphics));
}
// 绘制UI
drawUI(graphics);
}
}
```
2. 游戏对象建模与设计模式应用
2.1 游戏对象基类设计
```java
public abstract class GameObject {
protected int x, y;
protected int width, height;
protected int speed;
protected boolean alive = true;
protected BufferedImage image;
public GameObject(int x, int y, int width, int height) {this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public abstract void update();
public abstract void draw(Graphics2D g);
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
public boolean isColliding(GameObject other) {
return getBounds().intersects(other.getBounds());
}
// Getter和Setter方法
public int getX() { return x; }
public void setX(int x) { this.x = x; }
// ... 其他getter/setter
}
```
2.2 玩家飞机类实现
```java
public class PlayerPlane extends GameObject {
private static final int DEFAULT_SPEED = 5;
private int lives = 3;
private int firePower = 1;
private long lastFireTime = 0;
private final long FIRE_INTERVAL = 200; // 发射间隔(毫秒)
public PlayerPlane(int x, int y) {super(x, y, 50, 70);
this.speed = DEFAULT_SPEED;
loadImage();
}
private void loadImage() {
try {
image = ImageIO.read(getClass().getResource("/images/player.png"));
} catch (IOException e) {
// 备用绘制方案
System.err.println("无法加载玩家飞机图片");
}
}
@Override
public void update() {
// 边界检测
x = Math.max(0, Math.min(PlaneWar.SCREEN_WIDTH - width, x));
y = Math.max(0, Math.min(PlaneWar.SCREEN_HEIGHT - height, y));
}
public void move(int dx, int dy) {
x += dx speed;
y += dy speed;
}
public List<Bullet> fire() {
long currentTime = System.currentTimeMillis();
if (currentTime - lastFireTime < FIRE_INTERVAL) {
return Collections.emptyList();
}
lastFireTime = currentTime;
List<Bullet> newBullets = new ArrayList<>();
// 根据火力等级创建不同数量的子弹
int bulletX = x + width / 2 - 2; // 居中发射
newBullets.add(new Bullet(bulletX, y, firePower));
if (firePower >= 2) {
newBullets.add(new Bullet(bulletX - 15, y, firePower));
newBullets.add(new Bullet(bulletX + 15, y, firePower));
}
if (firePower >= 3) {
newBullets.add(new Bullet(bulletX - 30, y + 10, firePower));
newBullets.add(new Bullet(bulletX + 30, y + 10, firePower));
}
return newBullets;
}
@Override
public void draw(Graphics2D g) {
if (image != null) {
g.drawImage(image, x, y, width, height, null);
} else {
// 备用图形绘制
g.setColor(Color.CYAN);
g.fillRect(x, y, width, height);
}
}
public void takeDamage() {
lives--;
if (lives <= 0) {
alive = false;
}
}
public void upgradeWeapon() {
firePower = Math.min(firePower + 1, 3);
}
}
```
3. 敌机系统与对象池模式优化
3.1 敌机工厂类设计
```java
public class EnemyFactory {
private static final Random random = new Random();
public enum EnemyType {SMALL, MEDIUM, BOSS
}
public static Enemy createEnemy(EnemyType type, int level) {
int x = random.nextInt(PlaneWar.SCREEN_WIDTH - 50);
int y = -50; // 从屏幕上方出现
switch (type) {
case SMALL:
return new SmallEnemy(x, y, 1 + level / 3);
case MEDIUM:
return new MediumEnemy(x, y, 2 + level / 2);
case BOSS:
return new BossEnemy(x, y, 5 + level);
default:
return new SmallEnemy(x, y, 1);
}
}
public static Enemy createRandomEnemy(int level) {
EnemyType[] types = EnemyType.values();
EnemyType type = types[random.nextInt(types.length - 1)]; // 暂时排除BOSS
// 根据关卡调整出现概率
if (level % 5 == 0 && random.nextDouble() < 0.3) {
type = EnemyType.BOSS;
}
return createEnemy(type, level);
}
}
```
3.2 对象池优化内存管理
```java
public class ObjectPool {
private final Supplier creator;
private final Consumer resetter;
private final Queue pool;
private final int maxSize;
public ObjectPool(Supplier<T> creator, Consumer<T> resetter, int maxSize) {this.creator = creator;
this.resetter = resetter;
this.pool = new LinkedList<>();
this.maxSize = maxSize;
}
public T borrowObject() {
T obj = pool.poll();
if (obj == null) {
obj = creator.get();
}
return obj;
}
public void returnObject(T obj) {
if (pool.size() < maxSize) {
resetter.accept(obj);
pool.offer(obj);
}
}
public void preload(int count) {
for (int i = 0; i < Math.min(count, maxSize); i++) {
pool.offer(creator.get());
}
}
}
// 在GamePanel中使用对象池
public class GamePanel extends JPanel {
private ObjectPool bulletPool;
private ObjectPool enemyPool;
private void initializePools() {bulletPool = new ObjectPool<>(
() -> new Bullet(0, 0, 1),
bullet -> {
bullet.setAlive(false);
bullet.setX(0);
bullet.setY(0);
},
100
);
enemyPool = new ObjectPool<>(
() -> EnemyFactory.createRandomEnemy(gameLevel),
enemy -> enemy.setAlive(false),
50
);
bulletPool.preload(50);
enemyPool.preload(20);
}
}
```
4. 碰撞检测系统优化
4.1 空间分割碰撞检测
```java
public class CollisionSystem {
private static final int GRID_SIZE = 100;
private Map > grid;
public CollisionSystem() {grid = new HashMap<>();
}
public void addObject(GameObject obj) {
String key = getGridKey(obj.getX(), obj.getY());
grid.computeIfAbsent(key, k -> new ArrayList<>()).add(obj);
}
public void detectCollisions(List<GameObject> objects) {
grid.clear();
// 将对象分配到网格中
objects.forEach(this::addObject);
// 只检查相邻网格中的碰撞
for (List<GameObject> cellObjects : grid.values()) {
for (int i = 0; i < cellObjects.size(); i++) {
for (int j = i + 1; j < cellObjects.size(); j++) {
GameObject obj1 = cellObjects.get(i);
GameObject obj2 = cellObjects.get(j);
if (obj1.isColliding(obj2)) {
handleCollision(obj1, obj2);
}
}
}
}
}
private String getGridKey(int x, int y) {
int gridX = x / GRID_SIZE;
int gridY = y / GRID_SIZE;
return gridX + "," + gridY;
}
private void handleCollision(GameObject obj1, GameObject obj2) {
// 处理不同类型的碰撞
if ((obj1 instanceof Bullet && obj2 instanceof Enemy) ||
(obj1 instanceof Enemy && obj2 instanceof Bullet)) {
Bullet bullet = obj1 instanceof Bullet ? (Bullet) obj1 : (Bullet) obj2;
Enemy enemy = obj1 instanceof Enemy ? (Enemy) obj1 : (Enemy) obj2;
if (bullet.isPlayerBullet()) {
enemy.takeDamage(bullet.getDamage());
bullet.setAlive(false);
}
}
}
}
```
5. 游戏状态管理与场景切换
5.1 状态模式实现游戏状态管理
```java
public interface GameState {
void update(GamePanel panel);
void render(GamePanel panel, Graphics2D g);
void handleInput(KeyEvent e, GamePanel panel);
}
public class PlayingState implements GameState {
@Override
public void update(GamePanel panel) {
panel.updateGameObjects();
panel.checkCollisions();
panel.spawnEnemies();
if (!panel.getPlayer().isAlive()) {panel.setState(new GameOverState());
}
}
@Override
public void render(GamePanel panel, Graphics2D g) {
panel.renderGameObjects(g);
panel.drawHUD(g);
}
@Override
public void handleInput(KeyEvent e, GamePanel panel) {
// 处理游戏中的输入
}
}
public class GameOverState implements GameState {
private long gameOverTime;
public GameOverState() {this.gameOverTime = System.currentTimeMillis();
}
@Override
public void update(GamePanel panel) {
if (System.currentTimeMillis() - gameOverTime > 3000) {
// 3秒后返回菜单
panel.setState(new MenuState());
}
}
@Override
public void render(GamePanel panel, Graphics2D g) {
panel.renderGameObjects(g);
drawGameOverScreen(g, panel);
}
private void drawGameOverScreen(Graphics2D g, GamePanel panel) {
g.setColor(new Color(0, 0, 0, 128));
g.fillRect(0, 0, panel.getWidth(), panel.getHeight());
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 36));
String gameOver = "游戏结束";
int textWidth = g.getFontMetrics().stringWidth(gameOver);
g.drawString(gameOver, (panel.getWidth() - textWidth) / 2, panel.getHeight() / 2);
g.setFont(new Font("Arial", Font.PLAIN, 24));
String score = "最终得分: " + panel.getScore();
textWidth = g.getFontMetrics().stringWidth(score);
g.drawString(score, (panel.getWidth() - textWidth) / 2, panel.getHeight() / 2 + 50);
}
@Override
public void handleInput(KeyEvent e, GamePanel panel) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
panel.initializeGame();
panel.setState(new PlayingState());
}
}
}
```
6. 特效系统与粒子效果
6.1 粒子系统实现爆炸效果
```java
public class Particle {
private double x, y;
private double vx, vy;
private double life;
private final double maxLife;
private Color color;
private int size;
public Particle(double x, double y, Color color) {this.x = x;
this.y = y;
this.color = color;
this.maxLife = Math.random() 60 + 30; // 生命周期30-90帧
this.life = maxLife;
this.size = (int)(Math.random() 5 + 2);
// 随机速度方向
double angle = Math.random() Math.PI 2;
double speed = Math.random() 3 + 1;
this.vx = Math.cos(angle) speed;
this.vy = Math.sin(angle) speed;
}
public boolean update() {
x += vx;
y += vy;
vy += 0.1; // 重力效果
life--;
return life > 0;
}
public void draw(Graphics2D g) {
float alpha = (float)(life / maxLife);
g.setColor(new Color(color.getRed()/255f, color.getGreen()/255f,
color.getBlue()/255f, alpha));
g.fillOval((int)x, (int)y, size, size);
}
}
public class ParticleSystem {
private List particles;
public ParticleSystem() {particles = new ArrayList<>();
}
public void createExplosion(double x, double y, Color color, int count) {
for (int i = 0; i < count; i++) {
particles.add(new Particle(x, y, color));
}
}
public void update() {
Iterator<Particle> iterator = particles.iterator();
while (iterator.hasNext()) {
Particle p = iterator.next();
if (!p.update()) {
iterator.remove();
}
}
}
public void draw(Graphics2D g) {
particles.forEach(particle -> particle.draw(g));
}
}
```
7. 性能优化与最佳实践
7.1 使用Lambda表达式和Stream API优化代码
```java
public class GamePanel extends JPanel {
// 使用Stream API清理死亡对象
private void cleanupObjects() {
// 清理敌人
enemies.removeIf(enemy -> !enemy.isAlive() || enemy.getY() > getHeight());
// 清理子弹bullets.removeIf(bullet -> !bullet.isAlive() || bullet.getY() < -20);
// 使用并行流进行碰撞检测(大数据量时)
if (enemies.size() > 50) {
enemies.parallelStream().forEach(this::checkEnemyCollisions);
}
}
// 使用Lambda表达式进行事件处理
private void initializeEventHandlers() {
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT -> player.move(-1, 0);
case KeyEvent.VK_RIGHT -> player.move(1, 0);
case KeyEvent.VK_UP -> player.move(0, -1);
case KeyEvent.VK_DOWN -> player.move(0, 1);
case KeyEvent.VK_SPACE -> {
List<Bullet> newBullets = player.fire();
bullets.addAll(newBullets);
}
}
}
});
}
}
```
结论
通过本文的详细解析,我们不仅实现了一个功能完整的Java飞机大战游戏,还应用了多种设计模式和优化技巧。从基础的游戏框架搭建到高级的特效系统,每个环节都体现了良好的软件工程实践。
二次开发建议:
1. 添加网络功能实现多人游戏
2. 集成物理引擎实现更真实的运动效果
3. 使用OpenGL进行硬件加速渲染
4. 添加关卡编辑器和自定义关卡功能
5. 集成音频引擎增强游戏体验
希望本文能为您的游戏开发学习之路提供有价值的参考,鼓励您在此基础上进行更多创新和探索。
版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
以上就是Java飞机大战游戏源码解析与二次开发的完整内容,如果您觉得有帮助,欢迎点赞收藏!
好的,请看文章。
重读经典:《Java典型模块与项目实战大全》在云原生与AI时代的新生命
作为一名Java开发者,或许你的书架上曾有一本《Java典型模块与项目实战大全》或其类似的“实战大全”书籍。这类著作以其庞大的项目数量、覆盖从基础到企业级的完整技术栈而闻名,是无数程序员从入门到精通的“练功秘笈”。在云原生、容器化、微服务和AI大行其道的今天,我们是否还应拘泥于书中“古老”的代码?答案是否定的,但重读经典,并非为了照搬代码,而是为了萃取其设计思想,并用现代技术栈为其注入新的生命。本文将以现代视角,重新审视这份宝贵的“遗产”,探讨如何让其在新时期焕发活力。
一、 经典价值的再审视:为何它依然是基石?
《Java典型模块与项目实战大全》的核心价值在于其系统性的知识体系构建。它通过37个实战项目,清晰地勾勒出一条Java开发者成长的路径:
- 基础巩固:从Swing图形界面、文件操作到多线程编程,这些是Java语言的根基。尽管Swing已非主流,但其事件驱动模型的思想与现代前端框架(如Vue/React)的组件化思维一脉相承。
- Web入门:经典的JSP/Servlet + JDBC项目是理解B/S架构、MVC模式(Model1和Model2)的最佳教材。虽然现在已是Spring Boot的天下,但不懂Servlet,就无法深刻理解Spring MVC如何封装和简化了Web开发。
- 企业级技术:书中涉及的EJB、JMS、Web Service等项目,是理解分布式、事务、异步消息等企业级概念的钥匙。这些概念是当今微服务架构(如Spring Cloud)的理论基础。
这本书的价值并非过时,而是其内在的设计模式、架构思想和问题解决思路,这些是超越具体技术版本的永恒财富。
二、 从“项目实战”到“架构演进”:用现代技术重构经典
我们不应满足于运行通书中的代码,而应思考:如果今天用最新的技术栈重新实现这些项目,该怎么做? 这才是高质量的“实战”演练。
范例一:从“图书管理系统”到“云原生微服务”
- 经典实现:可能是一个单体的War包,使用JSP展示页面,JDBC直连数据库。
- 现代重构:
- 架构升级:拆分为微服务架构。使用Spring Boot构建“用户服务”、“图书服务”、“借阅服务”等独立模块,通过Spring Cloud Alibaba(Nacos服务发现、Sentinel流量防护)进行治理。
- 数据持久化:用Spring Data JPA 或 MyBatis-Plus 替代原生JDBC,极大提升开发效率。考虑引入缓存(Redis)提升查询性能。
- 部署与运维:将应用容器化(编写Dockerfile),使用Kubernetes进行编排和弹性伸缩。配置文件外部化到Nacos或Apollo,实现不同环境的一键切换。
- 前端分离:前后端彻底分离,后端提供RESTful API,前端由Vue 3或React 18构建,通过网关(如Spring Cloud Gateway)统一接入。
范例二:从“在线聊天室”到“实时交互应用”
- 经典实现:可能基于Java Socket或多线程,功能简单。
- 现代重构:
- 技术选型:采用WebSocket协议实现全双工通信。直接使用Spring框架对WebSocket的封装(如
@ServerEndpoint)或更高级的Netty框架来构建高性能的通信核心。 - 功能增强:集成LLM大语言模型API(如百度文心、ChatGPT),将聊天室升级为“AI智能助手”,实现智能问答、内容摘要等高级功能。这体现了将传统项目与AI能力结合的现代思路。
- 可扩展性:当单机性能成为瓶颈时,引入消息队列(RabbitMQ/Kafka)来解耦和削峰,使用Redis Pub/Sub实现多实例间的消息广播,轻松实现横向扩展。
三、 超越代码:现代开发者必备的配套技能
书中项目主要聚焦于业务代码实现。而今天的Java开发者,必须掌握一整套工程化工具链,这才是“高质量项目”的保障。
- 版本控制与协作:Git 是绝对主流。掌握特性分支工作流(如Git Flow)、提交规范,并熟练使用Gitee或GitHub进行团队协作。
- 依赖管理与构建:Maven 或 Gradle 已成为项目标配。理解依赖传递、冲突解决和多模块构建是现代项目结构的基础。
- 持续集成/持续部署:在Jenkins、GitLab CI或GitHub Actions中编写Pipeline脚本,实现代码提交后自动进行编译、测试、打包和部署,这是云原生时代的交付标准。
- API文档管理:用Swagger/OpenAPI 3.0替代手写文档,实现接口文档与代码的同步更新。
结语
《Java典型模块与项目实战大全》是一座富矿,但我们需要用新的工具去开采。它的价值不在于提供可复用的代码,而在于提供了一个个完整的“问题域”和经典的解决方案蓝图。我们的任务,就是站在这个巨人的肩膀上,运用Spring Boot、微服务、Docker、Kubernetes以及AI等现代利器和思维,对这些蓝图进行“现代化改装”。
这个过程,才是真正意义上的“实战演练”,它能让你不仅知其然(如何做),更知其所以然(为何这样演变),从而在技术飞速迭代的洪流中,建立起自己坚实而深邃的架构理解力。这,或许才是对经典著作最好的致敬与传承。
```java
public class IsInstanceDemo {
public static void main(String[] args) {
Object obj = "Hello World";
Number num = Integer.valueOf(42);
// 使用isInstance进行类型检查System.out.println("obj是String类型: " + String.class.isInstance(obj));
System.out.println("obj是Integer类型: " + Integer.class.isInstance(obj));
System.out.println("num是Number类型: " + Number.class.isInstance(num));
System.out.println("num是Double类型: " + Double.class.isInstance(num));
// 与instanceof操作符对比
System.out.println("obj instanceof String: " + (obj instanceof String));
System.out.println("num instanceof Number: " + (num instanceof Number));
}
@IgnoreAuth@PostMapping(value = "/login")
public R login(String username, String password, String captcha, HttpServletRequest request) {
UsersEntity user = userService.selectOne(new EntityWrapper<UsersEntity>().eq("username", username));
if(user==null || !user.getPassword().equals(password)) {
return R.error("账号或密码不正确");
}
String token = tokenService.generateToken(user.getId(),username, "users", user.getRole());
return R.ok().put("token", token);
}
@Override
public String generateToken(Long userid,String username, String tableName, String role) {
TokenEntity tokenEntity = this.selectOne(new EntityWrapper<TokenEntity>().eq("userid", userid).eq("role", role));
String token = CommonUtil.getRandomString(32);
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.HOUR_OF_DAY, 1);
if(tokenEntity!=null) {
tokenEntity.setToken(token);
tokenEntity.setExpiratedtime(cal.getTime());
this.updateById(tokenEntity);
} else {
this.insert(new TokenEntity(userid,username, tableName, role, token, cal.getTime()));
}
return token;
}
/**
* 权限(Token)验证
*/
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
public static final String LOGIN_TOKEN_KEY = "Token";
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//支持跨域请求
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
IgnoreAuth annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
} else {
return true;
}
//从header中获取token
String token = request.getHeader(LOGIN_TOKEN_KEY);
/**
* 不需要验证权限的方法直接放过
*/
if(annotation!=null) {
return true;
}
TokenEntity tokenEntity = null;
if(StringUtils.isNotBlank(token)) {
tokenEntity = tokenService.getTokenEntity(token);
}
if(tokenEntity != null) {
request.getSession().setAttribute("userId", tokenEntity.getUserid());
request.getSession().setAttribute("role", tokenEntity.getRole());
request.getSession().setAttribute("tableName", tokenEntity.getTablename());
request.getSession().setAttribute("username", tokenEntity.getUsername());
return true;
}
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try {
writer = response.getWriter();
writer.print(JSONObject.toJSONString(R.error(401, "请先登录")));
} finally {
if(writer != null){
writer.close();
}
}
// throw new EIException("请先登录", 401);
return false;
}
}
文章来源:https://blog.csdn.net/2509_93884217/article/details/153726021
技术支持:https://blog.csdn.net/2509_93862745/article/details/153719929
参考资料:https://blog.csdn.net/2509_93840300/article/details/153675344
文章来源①:https://blog.csdn.net/2509_93864009/article/details/153621512
技术支持②:https://blog.csdn.net/2509_93841531/article/details/153578986
参考资料③:https://blog.csdn.net/2509_93884111/article/details/153731307
更多推荐



所有评论(0)