会员可以在此提问,百战程序员老师有问必答
对大家有帮助的问答会被标记为“推荐”
看完课程过来浏览一下别人提的问题,会帮你学得更全面
截止目前,同学们一共提了 128777个问题
Python 全系列/第六阶段:数据库编程基础/python操作mysql 167楼
JAVA 全系列/第十八阶段:亿级高并发电商项目_架构/编码(旧)/电商:基于SpringSession实现登录系统 168楼
JAVA 全系列/第三阶段:数据库编程/MySQL数据库的使用 170楼
JAVA 全系列/预科阶段:职业规划/学习方法/程序员的基本素养和职业规划 171楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/IO 流技术(旧) 172楼

问题3.doc

老师看这个文件,前面两个文件少了一些题

C++特训班/第三阶段:进阶篇/进阶篇 173楼

老师,为什么在程序运行后,能够正常运行,但总是出现异常呢?我根据老师的源码检查了多次,没有发现哪里有问题啊?

package cn.sxt.game;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

/**
 * 容器和数组产生多发炮弹
 * @author Administrator
 *
 */
public class MyGameFrame extends Frame{
	
	Image planeImg = GameUtil.getImage("images/plane.png");
	Image bg = GameUtil.getImage("images/bg.jpg");
	//创建飞机的对象,并调用Plane
	Plane plane = new Plane(planeImg,250,250);
	//创建炮弹的对象,并调用Shell
	Shell[] shells = new Shell[50];
	
	
	@Override
	public void paint(Graphics g) {//自动被调用。g变量相当于画笔
		
		g.drawImage(bg, 0, 0, null);
		plane.drawSelf(g);//画飞机
		
		//画50个炮弹
		for(int i = 0;i<shells.length;i++) {
			shells[i].draw(g);
			
			//飞机和炮弹的碰撞检测
			boolean peng = shells[i].getRect().intersects(plane.getRect());
			
			if(peng) {
				plane.live = false;
			}
		}
	}
	
	//定义一个内部类并继承线程
	class PaintThread extends Thread{
		
		@Override
		public void run() {
			while(true) {
				repaint();//重画窗口
			
			
				try {
					Thread.sleep(40);//1秒等于1000毫秒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			
			}	
		}
	}
	
	//定义键盘监听的内部类,并继承KeyAdapter
	class KeyMonitor extends KeyAdapter{

		@Override
		//按一下键,会获得那些信息
		public void keyPressed(KeyEvent e) {
			plane.addDirection(e);//调用Plane中的按下方法
		}

		@Override
		//释放按键,会获得哪些信息
		public void keyReleased(KeyEvent e) {
			plane.minusDirection(e);
		}
	
	}
	
	
	/**
	 * 初始化窗口
	 */
	public void launchFrame() {
		this.setTitle("2D经典");//窗口名称
		this.setVisible(true);
		this.setSize(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);//设置窗口大小
		this.setLocation(300, 300);//设置窗口位置
		
		//定义窗口关闭程序
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);//表示直接关闭
			}
		});
		
		new PaintThread().start();//启动重化窗口的线程
		addKeyListener(new KeyMonitor());//给窗口增加键盘的监听	
		
		//初始化50个炮弹
		for(int i = 0;i<shells.length;i++) {
			shells[i] = new Shell();
		}
	
	
	}
	
	public static void main(String[] args) {
		MyGameFrame f = new MyGameFrame();
		f.launchFrame();
	}
	
	private Image offScreenImage = null;
	
	public void update(Graphics g) {
		if(offScreenImage == null)
			offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);//窗口的宽度和高度
		
		Graphics gOff = offScreenImage.getGraphics();
		paint(gOff);
		g.drawImage(offScreenImage, 0, 0, null);
	}
}

	

GIF.gif

JAVA 全系列/第一阶段:JAVA 快速入门/飞机大战小项目训练 176楼

说句玩笑话,关于ThreadLocal 分析的视频是认真做的吗?

 

  1. 关于set方法和get方法的顺序问题,视频先介绍的set方法

    set.png

    视屏并未说明为何3个ThreadLocalMap中的Number为何不是同一个对象?因为我们要保证的ThreadLocalMap 键值对对象中的value值为不同的?实际上传入的value对象才是关键吧。

    此set方法只是展示了:将value设置到自己线程的ThreadLocalMap键值对中,其中key为线程对象本身,值为value对象;但是并不能判断对象(图示中的number为不同的对象)

     

2.实际上set方法之所以设置的value为不同的对象,是因为get方法,

QQ截图20190109180939.png

视频中分析的get方法通过线程对象得到value 而get方法源码为:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

正是由于第一次执行get方法时,ThreadLocalMap map = getMap(t);中map为null

所以执行 return setInitialValue();其中setInitialValue()方法源码为:

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

是因为第一次执行setInitialValue()方法时,返回的是执行 ,T value = initialValue();中value的值

而 initialValue()方法是我们自己实现的;

也就是说,每个ThreadLocalMap键值对的值的初始值(对象),都是通过initialValue()方法新创建不同对象。因为是不同的对象,但是状态相同,所以才有每个副本的值相同吧.

所以视频的源码分析是故意没讲完全?

    

JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程和并发编程(旧) 177楼
JAVA 全系列/第六阶段:项目管理与SSM框架/Maven 178楼
Python 全系列/第一阶段:Python入门/Python入门(动画版) 180楼

百战程序员微信公众号

百战程序员微信小程序

©2014-2024 百战汇智(北京)科技有限公司 All Rights Reserved 北京亦庄经济开发区科创十四街 赛蒂国际工业园
网站维护:百战汇智(北京)科技有限公司
京公网安备 11011402011233号    京ICP备18060230号-3    营业执照    经营许可证:京B2-20212637