简介:这是一份面向高校Java课程教学的实验指导书,紧紧围绕《Java语言》课程68学时的上机实践安排,将内容划分为Java基础、Java核心和企业级应用选学三大模块,便于不同阶段学习者按需选用。全书共有八个实验,前六个实验依次覆盖JDK安装与环境变量配置、基本语法练习、面向对象编程、包与接口及异常处理、数组和向量与字符串操作、常用系统类应用,后面还补充了图形用户界面设计、图形与多媒体处理、多线程等方面的进阶练习,每个实验都给出实验目的、实验要求和具体上机内容,能够帮助读者从零开始逐步完成Java编程能力的系统训练。资源包仅包括一份文档文件,大小约为五百三十千字节,内容组织清晰,目录结构完整,既适合作为课堂配套实验教材,也适合自学Java的读者按实验章节逐一实践。目前已有二百三十八人浏览学习,对于正在学习Java基础或准备课程实验的高校学生具有直接的参考价值和实操指导意义。
1. Java实验第一步就翻车:Applet和JDK版本的死结
手里拿到一本《Java程序设计实验指导书》,翻开实验1就是SDK的下载与安装、设置环境变量、安装JCreator,后面跟着Applet小程序、窗口菜单、图形与多媒体。按这套java实验顺序走,前8个实验是主线,后面还排了流与文件、线程、网络编程和JDBC/JSP选学,总共68学时。问题在于,指导书写得很早,默认你装的是那个年代的SDK,里面的Applet、appletviewer这些工具链,到了新版JDK里早就被移除了。如果谁直接装个最新版JDK,实验3、6、8、9里那些以Applet为入口的程序基本一个都跑不起来,这是java实验环境搭建最常见的翻车现场。这篇文章就按这份指导书把环境从头装一遍,从版本选择、环境变量、编译运行到每个实验的落地练习和排错,照着走到能跑通为止。
2. 安装JDK与配置环境变量:版本选错、三个变量怎么填
实验1的标题叫“Java运行环境的安装、配置与运行”,实际就两件事:装一个能用的JDK,把环境变量配到命令行敲得出来。这一章把版本选择和三个变量的坑一次讲完,避免在第2章就卡住。
2.1 先定版本:指导书里的Applet决定你装哪个JDK
指导书通篇在讲SDK,这个词在老教材里就是JDK。那个年代的教学配套工具是JCreator和Applet,Applet应用小程序要跑起来,依赖的是JDK自带的插件机制和appletviewer这个命令行工具。现在的新版JDK已经把这些东西移除了:JDK 9开始弃用Applet API,JDK 11开始移除appletviewer工具,连java.applet包都找不到了。如果装的是这种新版本,实验3“创建Applet应用小程序”、实验6“了解Applet生命周期”、实验8“插入图像与播放音乐”、实验9“电闪雷鸣的动画”这些练习都会变成无源之水。
我的建议是:要完整复现这本指导书的全部实验,装JDK 8,这是最后能原生跑Applet和appletviewer的主流大版本;如果只是想做实验2到实验7里的语法、面向对象和Swing窗口练习,装企业里常见的LTS版本也没有问题。判断标准很简单,翻开目录看有没有Applet字样,有就回到JDK 8。这个决定做完,后面所有配置文件都围绕同一套JDK,不折腾。
| 实验范围 | 推荐JDK | 原因 |
|---|---|---|
| 完整复现指导书所有实验,含Applet、appletviewer、AudioClip | JDK 8 | 最后一个完整支持Applet与appletviewer的主流版本 |
| 只做基本语法、面向对象、Swing窗口、流、线程、网络 | 新版LTS | 语法和类库兼容,练习不受影响 |
| 需要结合JDBC和Servlet选学实验 | 按教材配套版本选 | 老教材配老版本,新教材配新版本 |
下载安装时注意两点。一是JDK 8的安装包现在仍然能方便地拿到,下载msi或exe安装包后,安装路径不要带中文,建议装在纯英文目录下,比如C盘根目录按版本号建目录,后面配JAVA_HOME时少很多乱码和路径解析问题。二是装之前先盘点系统里已有的Java,很多电脑装了不止一个JDK或JRE,哪怕用命令行查不到,控制面板的“程序和功能”里也要看一眼,多版本并存时环境变量一旦配错,出现的就是“java能用、javac不能用”这种让人抓狂的现象。
2.2 配置JAVA_HOME、PATH与CLASSPATH:三个最容易写错的路径
指导书实验1专门有一节“设置环境变量”,讲的就是这三个变量。它们的分工和常见坑位我用一张表压平:
| 变量 | 作用 | 推荐值 | 最容易踩的坑 |
|---|---|---|---|
| JAVA_HOME | 给其他工具和IDE指路,指明JDK安装根目录 | C:\Program Files\Java\jdk1.8.0_202 | 写成了bin目录,或写到最后带了一个反斜杠 |
| PATH | 让命令行直接识别java和javac命令 | 新增一行%JAVA_HOME%\bin | 新值追加在旧JDK路径后面,被旧版本抢先 |
| CLASSPATH | 告诉JVM到哪里找class文件 | 新版JDK可不配;要配就只配. | 照老教材抄了一长串dt.jar和tools.jar,反而引入冲突 |
Windows下配置的常规操作是:右键“此电脑”进入系统属性,打开“高级系统设置”,在“环境变量”里新建JAVA_HOME,变量值填JDK安装根目录;然后在系统变量PATH里新增一行%JAVA_HOME%\bin;CLASSPATH这一项,我会直接告诉你不要照抄老教材那一长串。JDK 5以后默认就会把当前目录加入类搜索路径,CLASSPATH配成空的反而干净,强行配一个.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar上去,一旦路径里带个空格或变量展开异常,编译能过、运行报错的情况就会出现,非常玄学。
提示:不要用
setx PATH "%PATH%;..."这种方式追加PATH。setx会把当前PATH整体写入用户变量,随后系统PATH再发生变化时,新旧路径会互相叠加,命令行环境变得越来越不可控。老老实实在系统属性界面里编辑,最稳。
macOS或Linux下配置的常规做法是编辑shell配置文件,在~/.bashrc或~/.zshrc里追加:
export JAVA_HOME=/usr/lib/jvm/jdk-8u202 export PATH=$JAVA_HOME/bin:$PATH # export CLASSPATH=. # 需要时可以打开,新版默认不需要配置文件的逻辑是:先定义JAVA_HOME指向JDK根目录,再把$JAVA_HOME/bin放在PATH前面,让它优先于系统自带或其他途径装进来的Java。CLASSPATH这行默认注释掉,等遇到具体问题再按需打开,不要把老教材的dt.jar写法搬到新环境里。
配置完关闭终端重新打开,环境变量才会重新加载,这一点常被忽略。
2.3 验证安装:java -version和javac -version一起看
环境变量配完,先别急着写代码,用两条命令确认环境真的干净:
java -version javac -version第一条看的是运行时,第二条看的是编译器。两条命令输出的版本号必须一致,比如都是1.8.0_202。java和javac来自同一套JDK,说明JAVA_HOME和PATH没有指向两套不同的安装;如果版本对不上,说明PATH里有陈旧的其他JDK路径在抢位置。
再配合一条echo看路径变量是否展开正确:
echo %JAVA_HOME% echo %PATH%Windows下如果JAVA_HOME显示正常而java命令还是找不到,问题多半在PATH里%JAVA_HOME%\bin这一行没生效,重开终端再试。到这里还没写代码,但实验1里“SDK的下载与安装、设置环境变量”这两步已经走完,下一步才是真正动手编译和运行程序。
3. 编译运行与JCreator:命令行和IDE两个入口都要打通
指导书的实验1从“编写、编译、运行Java源程序”一直讲到“安装、使用JCreator软件”,说明当年上课是命令行和IDE两条腿走路。这一章把两条路径都走一遍,重点解决“代码写对了但跑不起来”的常见病因。
3.1 javac编译、java运行:入口、类名和包名
Java程序的运行入口是main方法,javac负责把源码编译成字节码,java负责启动虚拟机执行。
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, Java 实验环境"); } }编译和运行命令:
javac HelloWorld.java java HelloWorld这里的约束是:public类的类名必须和文件名完全一致,大小写敏感,HelloWorld.java里只能有一个public类。javac执行后生成HelloWorld.class,java命令后面带的是类名,不是文件名,所以不带.class后缀。新手在这里的经典错误是写成java HelloWorld.class,虚拟机会直接报找不到主类。
一旦代码里声明了包名,编译运行方式就变了:
javac -d . demo/HelloWorld.java java demo.HelloWorld-d .表示按包名结构把class文件输出到当前目录,生成demo/HelloWorld.class;运行时用全限定类名demo.HelloWorld。我在做这类练习时,只要源码里出现package,编译命令必定带-d,不带的话class文件会堆在当前目录下,运行时类搜索路径按包结构找不到,报NoClassDefFoundError,又是一个黑匣子问题。
3.2 Applet实验:没有main入口的程序怎么编译怎么跑
Applet类没有main方法,它继承java.applet.Applet,由浏览器或appletviewer容器加载并调用生命周期方法。
import java.applet.Applet; import java.awt.Graphics; public class HelloApplet extends Applet { public void paint(Graphics g) { g.drawString("Applet 跑通了", 20, 40); } }编译Applet和编译普通类一样,javac HelloApplet.java,但运行方式完全不同。需要准备一个HTML页面引用它:
<applet code="HelloApplet.class" width="300" height="200"></applet>然后用JDK自带的appletviewer打开这个HTML:
appletviewer HelloApplet.html这才是全套旧式流程。指导书实验1里有一节“浏览SDK自带的Applet应用小程序”,实践方法就是在JDK安装目录下的demo目录里找现成的HTML和class文件,逐个用appletviewer打开,观察每个小程序的界面和交互。注意code属性写的是编译后的类名,如果Applet类在某个包下,这里必须写全限定类名包名.类名,只写类名会直接ClassNotFound。
注意:别指望用现代浏览器直接打开那个HTML。浏览器早就停止对Java插件的支持,页面会显示一片空白。在JDK 8环境里,appletviewer才是最可控的运行方式。
3.3 JCreator:老教学工具的安装与替身
JCreator是那个年代Windows下非常轻量的Java集成开发环境,安装包小,界面朴素,对内存要求低,实验课机房特别喜欢它。按指导书要求的流程,实际操作是下载安装包、一路下一步完成安装,然后在IDE的设置里指定JDK目录和编译器版本。它的核心功能其实只有三样:编辑源码、调用javac编译、调用java运行,和命令行做的事情完全一致。
不过这个工具年代比较久远,新版JDK在它那里可能识别不了。我现在的习惯是:理解它的存在是为了降低第一次接触Java的恐惧感,但新装环境时已经不太建议用它,随便换一个现代轻量编辑器或IDE,同样配置好JDK路径就能替代。装IDE本身不是实验目的,实验目的是把“编译—运行”这个闭环打通。用哪个工具不重要,重要的是脑子里清楚底下调用的还是javac和java这两个命令。
4. 从语法到GUI实验:指导书前8个实验怎么一步步跑通
指导书的前8个实验是一条清晰的递进路线:环境→语法→面向对象→包与接口异常→数组字符串→常用类→窗口界面→图形多媒体。这一章按这条线讲几个关键实验怎么落地,每个实验给出能直接编译运行的代码骨架,照着手改参数,比对着书抄一遍更有用。
4.1 基本语法与面向对象:变量、继承、多态这样练
实验2练的是变量声明、作用域、关系运算、选择语句和循环语句,实验3进入面向对象。很多人在继承和多态这里第一次卡住,核心在于理解“父类引用指向子类对象”这句话。
class Animal { protected String name; Animal(String name) { this.name = name; } public void speak() { System.out.println(name + " 发出声音"); } } class Dog extends Animal { Dog(String name) { super(name); } public void speak() { System.out.println(name + " 汪汪叫"); } } public class TestExtends { public static void main(String[] args) { Animal a = new Dog("小黄"); a.speak(); } }这段代码把继承、构造方法、方法重写、多态四个知识点全串起来了。super(name)调用父类构造器完成name的初始化,class Dog extends Animal建立继承关系,子类重写speak()后,父类引用a实际指向Dog对象,调用的是子类版本的方法。编译运行命令是常规的:
javac TestExtends.java java TestExtends输出结果应该是小黄 汪汪叫。如果输出的是小黄 发出声音,说明重写没生效,多半是子类方法签名和父类不一致,比如漏了public或者参数列表不匹配。我练这一类题目时习惯故意把某个方法签名改错一次,亲眼看看编译器的报错信息,比死记“重写规则”印象深得多。
实验3还有个题目是“编写显示当前日期和时间的程序”,当年通用做法是用java.util.Date配合SimpleDateFormat格式化输出,这也是后面实验6“使用日期类”的铺垫。
4.2 包、接口与异常:先编译出包结构再谈其他
实验4是包、接口与异常处理,多数人在自定义包这一步翻车,原因是不知道编译后class文件必须和包名对应的目录结构保持一致。先建一个带包名的类:
package demo.util; public class Calc { public int add(int a, int b) { return a + b; } }源文件放在demo/util/Calc.java,从src根目录编译并生成包结构:
javac -d . demo/util/Calc.java java demo.util.Calc # 此时还没有main方法,这行先不用执行-d .把生成的class按demo/util/目录存放,之后在别的类里用import demo.util.Calc;才能被正确找到。接口和异常两个知识点建议合在一起练,接口定义行为规范,异常处理负责兜底。
public class TestException { public static void main(String[] args) { try { int result = 10 / 0; System.out.println(result); } catch (ArithmeticException e) { System.out.println("除数不能为 0:" + e.getMessage()); } finally { System.out.println("finally 块总会执行"); } } }try块里放可能出错的代码,catch捕获具体异常类型,finally保证无论是否异常都会执行的收尾逻辑。实验4的练习里,我建议把ArithmeticException换成Exception再跑一次,观察编译器对“捕获过宽”的提示,体会异常机制里“具体类型优先”的原则。
4.3 数组、向量和字符串:老教材数据结构的正确替换
实验5叫“数组、向量和字符串”,实验6叫“常用系统类的使用”。教材里的向量是java.util.Vector,这是JDK 1.2时代的动态数组,方法是addElement、elementAt这一套老接口。现在写代码常用ArrayList取代Vector,语法更简洁,但做实验时用哪个都行,关键是理解动态数组的容量扩展逻辑。下面这段同时演示字符串操作和常用类:
import java.util.Date; import java.util.Vector; public class CommonClasses { public static void main(String[] args) { System.out.println("圆周率:" + Math.PI); System.out.println("取较大值:" + Math.max(3, 7)); System.out.println("当前时间:" + new Date()); Vector<String> v = new Vector<String>(); v.add("A"); v.add("B"); for (int i = 0; i < v.size(); i++) { System.out.println(v.get(i)); } String s = " hello java "; System.out.println("去掉空格转大写:" + s.trim().toUpperCase()); } }这段里出现了Math、Date、Vector、String四个类,正好覆盖实验5和实验6的主要对象。Math.max是静态方法直接调用,trim()去掉两侧空格后toUpperCase()转大写,字符串是不可变对象,这两个方法都返回新字符串,原字符串不变。实验6里“使用数学函数类”的练习基本就是Math的静态方法全家桶,“使用日期类”则要会格式化输出:
import java.text.SimpleDateFormat; import java.util.Date; public class DateFormatDemo { public static void main(String[] args) { Date now = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(sdf.format(now)); } }yyyy四位年份、MM两位月份、dd两位日期,大小写不能乱,HH是24小时制,hh是12小时制。这个格式化pattern是我每次写日期类都容易记混的地方,实验课里多写几遍就记住了。
4.4 窗口、事件与多媒体:Swing和Applet图形实验
实验7进入图形用户界面,实验8是图形与多媒体。Swing窗口的落地练习可以收敛到一个最小例子:创建一个窗口,放一个按钮,点按钮触发事件。这个骨架覆盖了GUI创建和事件处理两个考点。
import javax.swing.JButton; import javax.swing.JFrame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class SimpleWindow extends JFrame { public SimpleWindow() { setTitle("Java GUI 实验"); setSize(400, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton btn = new JButton("点我"); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("按钮被点击了"); } }); add(btn); setVisible(true); } public static void main(String[] args) { new SimpleWindow(); } }setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)是窗口关闭时让进程结束的关键,漏掉这行窗口关掉但Java进程不退。事件监听器用了匿名内部类,这是实验7“使用匿名类”题目的标准写法。正方形按钮会占满整个窗口,因为add(btn)默认用的是BorderLayout的CENTER区域,想看到布局效果可以换成JPanel加上FlowLayout再添加多个组件。
实验8的图像与多媒体,沿用Applet路线。Applet里加载图片和播放音频的标准做法是使用getImage和play方法:
import java.applet.Applet; import java.awt.Graphics; import java.awt.Image; public class MediaApplet extends Applet { private Image img; public void init() { img = getImage(getCodeBase(), "demo.jpg"); play(getCodeBase(), "demo.au"); } public void paint(Graphics g) { g.drawImage(img, 0, 0, this); } }getCodeBase()返回的是Applet类所在目录的URL,图片和音频文件要和class文件放在同一目录下,路径写错时图片区域会空白但不报错。play只播放一次音频,想循环播放可以换成AudioClip对象的loop()方法。运行方式和3.2节一样,写HTML再交给appletviewer。做完这个实验,图形类Graphics的画线、画圆、填充这些基础方法就都过了一遍。
5. 避坑排查:java实验最常见的几个翻车现场
环境类问题有个特点:报错信息千奇百怪,但根因往往就那么几个。这一章把我在装java实验环境时反复遇到过的问题按“现象、原因、解决”拆开,每一条都是踩过的坑。
5.1 环境变量配了不生效
现象:在系统属性里配好了JAVA_HOME和PATH,重新打开命令行执行java -version,仍然提示“不是内部或外部命令”。
原因:最常见的是没有重新打开终端。环境变量在终端启动时读取,旧窗口里还保留着之前的进程环境。另一个常见原因是PATH里%JAVA_HOME%\bin写成了%JAVA_HOME%bin,少了反斜杠,路径展开后连在一起根本不存在。
解决:先重开一个命令行窗口再试一次。如果还不行,执行echo %PATH%看看变量展开后的实际路径是否正确。系统属性界面里编辑PATH时,建议用“新建”而不是在一行里追加,避免不小心把两个路径粘在一起。这个问题通常在两分钟内解决,但每次都会遇到。
5.2 java和javac版本对不上
现象:java -version显示1.8.0_202,javac -version显示其他版本,或者反过来。
原因:PATH里存在多个JDK路径,系统按顺序找到哪个用哪个。常见场景是先装了新版JDK,卸载时没清干净PATH,后来又装了JDK 8,两条路径都在PATH里排队。
解决:打开系统属性的PATH编辑界面,把%JAVA_HOME%\bin或C:\...\jdk1.8.0_202\bin移动到其他Java相关路径的最前面,让JAVA_HOME指定的版本优先。把不再使用的JDK路径直接删掉是最彻底的办法。这个问题不解决,后面编译出来的class可能和运行时的JVM不兼容,报出UnsupportedClassVersionError,那又是新一轮排查。
5.3 Applet报ClassNotFound
现象:appletviewer打开HTML,控制台提示ClassNotFoundException,Applet区域一片空白。
原因:三种可能:HTML的code属性写错了类名;class文件根本没有生成或不在HTML所在目录;Applet类在包里但code没写全限定名。Applet用浏览器插件要拼插件的安全策略,现在插件没了,报错定位反而更依赖appletviewer的输出。
解决:先确认javac编译成功且HelloApplet.class文件和HTML在同一目录。带包名的类,code属性写包名.HelloApplet而不是HelloApplet.class。执行appletviewer时同样注意当前目录,让控制台里的路径和实际文件位置一致。写HTML时width和height别省略,有些浏览器插件对缺省值处理得很奇怪。
5.4 中文乱码
现象:源码里的中文注释、中文字符串编译后显示成乱码,或者编译直接报“编码GBK的不可映射字符”。
原因:Windows下默认编码是GBK,代码文件用UTF-8保存时,javac按系统默认编码去读,自然读歪了。反过来,代码文件是GBK而javac用UTF-8参数去编,也会乱。
解决:统一编码。源代码文件全部保存为UTF-8,编译时显式指定编码:
javac -encoding UTF-8 HelloWorld.java如果文件已经是GBK编码,就先保存为UTF-8再编译。IDE里也把项目编码设为UTF-8,三处统一后乱码问题基本绝迹。这个坑在实验1写“Hello Java”中文版时就会出现,不处理会一直延续到实验8的图形界面标题。
5.5 CLASSPATH配错引发的奇怪问题
现象:代码能编译,运行时抛NoClassDefFoundError,或者程序加载了一个本地根本没见过的旧类版本,行为异常。
原因:CLASSPATH被照抄老教材配上了一长串dt.jar和tools.jar,一旦这些路径不存在或者展开出错,类加载器就找不到当前目录的class;另一种情况是项目目录下多了一个旧版本的jar包,CLASSPATH把它的路径排在了前面,类被旧版本抢先加载。
解决:新版JDK里把CLASSPATH留空,让JVM走默认的类搜索逻辑,当前目录自动在搜索范围内。如果确实需要指定额外类路径,用小写-cp参数在命令行传入:
java -cp .;lib/commons-lang3.jar;lib/mysql-connector.jar com.demo.Main-cp紧跟冒号或分号分隔的路径列表,第一项.表示当前目录。这个参数只在当前命令生效,不会污染全局环境,比改系统变量可控得多。我遇到NoClassDefFoundError时第一反应永远是去检查运行时类路径,而不是怀疑代码逻辑。
6. 装完怎么验证:用双入口综合实验一次验完
环境装完、避坑也看了,最后需要一个“一票否决”式的验证,确认JDK、编译、运行、AWT、事件、Applet这一整条链路都活着。我选的验证材料是实验9里的“Applet与Application合并运行”——同一个类,既当Applet跑,又当普通Application跑,两边都能弹窗口、响应事件,整套环境才算验完。
6.1 Applet与Application合并运行的验证步骤
import java.applet.Applet; import java.awt.Button; import java.awt.Color; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class DualApp extends Applet implements ActionListener { private Button btn; public void init() { btn = new Button("改变背景色"); btn.addActionListener(this); add(btn); } public void actionPerformed(ActionEvent e) { setBackground(new Color((int) (Math.random() * 0xFFFFFF))); } public static void main(String[] args) { Frame f = new Frame("Applet 与 Application 合并"); DualApp app = new DualApp(); app.init(); f.add(app); f.setSize(400, 300); f.setVisible(true); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } }编译后准备HTML页面:
<applet code="DualApp.class" width="400" height="300"></applet>然后分两步运行验证。
第一步,验证Application入口:
javac DualApp.java java DualApp应该弹出一个标题为“Applet 与 Application 合并”的窗口,里面有一个“改变背景色”按钮,每次点击窗口背景色都会随机变化。
第二步,验证Applet入口:
appletviewer DualApp.html应该出现另一个窗口,按钮行为完全一致。这条命令能跑通,说明Applet API、appletviewer工具、AWT组件包全部正常。如果第一步通过而第二步失败,问题出在Applet侧;如果两个入口都正常,这套环境已经覆盖了指导书前8个实验的全部底层依赖。把这两步做成一个固定流程的意义在double check:光java -version能出版本号不算数,本指导书里真正的环境验收标准是“带GUI的Applet跑得起来、事件响应正常”。
从那以后我每次装完Java环境,再也不敢只敲一遍java -version就收工,一定强制走一遍“编译一个带继承关系的类 + 跑一个Applet窗口”才算数。这套验证流程救过我太多次,也帮不少同学当场揪出过“环境装了但class文件根本没生成”的隐蔽问题。希望这篇文章能帮你在java实验环境这条路上少走几个来回,一次装完,直接跑通。
本文还有配套的精品资源,点击获取