一、基本概念
先创建包然后再建立相应的类最后建立主方法
用 public class 类名{}
表示 类
用public static void main(String[] args){}
表示主方法
public
公开给其他类, class
类声明
public 表示可被访问的类
private 表示在一定范围内可以被访问的类
public class Demo01 {
public static void main(String[] args){
System.out.println("月上柳梢头");//月上柳梢头
String a="China";
System.out.println(a);//China
}
}
程序从 public static void main(String[] args){}
开始执行
.java
编译后成为.class
文件
System.out.println("1");
System.out.println("2");
/*
1
2
*/
System.out.println("");
输出后以新的一行开始
System.out.print("1");
System.out.print("2");
//12
类必须实例化成为对象后才能使用
实例化:类名 对象名 = new 类名( 参数 ) ;
InputStreamReader isr=new InputStreamReader(System.in);
//isr是一个实例了。System.in意思是从键盘读取
//求两个数的和并输出
import java.io.BufferedReader;//导入包
import java.io.IOException;
import java.io.InputStreamReader;
/*上面三行可以换为
import java.io.*;
*/
public class Demo02 {
static int a,b;//成员变量.静态的方法无法引用非静态的变量,所以需要加上 static
public static void input(){
try {
System.out.println("Please enter two integers :");
InputStreamReader isr = new InputStreamReader(System.in); //isr是一个实例了。System.in意思是从键盘读取
BufferedReader br = new BufferedReader(isr);//br是一个实例。放入缓冲区
String s = br.readLine();//之前报错,添加主方法后的异常抛出后可正常运行
String[] tmp = s.split(",");//复制此条代码到别处时:String[] tmp=s.split(",");以 , 分隔
a = Integer.parseInt(tmp[0]);//强制转换为整型
b = Integer.parseInt(tmp[1]);
System.out.println("a=" + a);
System.out.println("b=" + b);
}catch(Exception e){
e.printStackTrace();
}//异常处理
}
public static int sum(){//成员方法
return a+b;
}
public static void main(String[] args) {//类的主方法
try {
Demo02 d=new Demo02();//实例化
d.input();
System.out.println("sum=" + d.sum());
}catch(Exception e){
e.printStackTrace();
}//异常处理
}
}
二、类与对象
当在设计类时,要记得对象是靠类的模型塑造出来的。
实例变量——对象本身已知的事物
方法——对象可以执行的动作
类是对象的蓝图 根据某类创建出的对象都有自己的实例变量。例:你可以用按钮类来创建许多大小、颜色、文字等不同的按钮。
main的两种用途:
- 测试真正的类
- 启动你的Java程序
真正的Java程序只会让对象与对象进行交互(相互调用方法)。
不要总是呆在main()中
猜数字游戏
摘要:
这个游戏涉及到game和player两个对象。game会产生0-9之间的随机数字,而3个player对象会猜测该数字
类:
- GuessGame.class
- Player.class
- GameLauncher.class
程序逻辑:
- GameLauncher这个类带有main()方法,是程序的入口。
- main()中会创建出GuessGame对象,并调用它的starGame()方法
- startGame()方法是游戏的起点。它会创建3个player,然后挑出要猜测的随机数字。它会要求player猜测并检查结果,过程会被列出来。
三、primitive主数据类型和引用(认识变量)
类型:
- 整数:short(2字节) int(4字节) long(8字节)
- 字节:byte
- 浮点数:float(4字节) double(8字节)
- 字符: char(2字节)
- 布尔:boolean
- 字符串数组:Strng [ ]
关于字符串的比较
String a="a",b="a";
if(a.equals(b)){
System.out.println("a=b");
}//a=b
float f=5.20f
如果不加f,所有带有小数点的值都会被Java当做double 处理。
条件测试的类型只能是boolean型
int a=1;
if(a){
}
上述代码C语言中成立,但JAVA会报错。下例是正确的。
boolean isHot=true;
if(isHot){
}
对象引用
对象引用变量保存的是存取对象的方法。它并不是对象的容器,而是类似指向对象的指针。或者可以说是地址。但在Java中我们不会也不该知道引用变量中实际装载的是什么,它只是用来代表单一的对象。只有Java虚拟机才会知道如何使用引用来取得该对象。
举个例子
Dog d = new Dog();
数组也是对象
int[] nums;//声明一个int数组变量
int i;
nums= new int[4];//创建大小为4的数组,并将它赋值给nums
nums[0]=1;
nums[1]=3;
nums[2]=4;
nums[3]=9;
for(i=0;i<4;i++)
System.out.println("nums["+i+"]"+"="+nums[i]);
/*
nums[0]=1
nums[1]=3
nums[2]=4
nums[3]=9*/
创建Dog数组
public class Dog {
String name;
public void bark(){
System.out.println(name+": 汪汪");
}
}
public class DogTestDrive {
public static void main(String[] args){
//创建Dog对象
Dog d1=new Dog();//记得加()
d1.name="Bart";
d1.bark();
System.out.println();
//创建Dog数组
Dog[] myDogs=new Dog[3];
//关门放狗.....这一步不能少
myDogs[0]=new Dog();
myDogs[1]=new Dog();
myDogs[2]=new Dog();
/*
int x;
for(x=0;x<myDogs.length;x++)
myDogs[x]=new Dog();
*/
//通过数组引用存取Dog
myDogs[0].name="Fido";
myDogs[1].name="Mike";
myDogs[2]=d1;
int i=0;
for(;i<myDogs.length;i++){
myDogs[i].bark();
}
}
}
/*
Bart: 汪汪
Fido: 汪汪
Mike: 汪汪
Bart: 汪汪*/
四、方法操作实例变量(对象的行为)
类描述的是对象知道什么与执行什么
方法会运用形参,调用的一方会传入实参。
实参是传给方法的值。当它传入后就成了形参。参数和局部变量是一样的。它有类型与名称,可以在方法内运用
public class Dog {
String name;
int age;
public void bark(int numOfBarks) {
while(numOfBarks>0)
{
System.out.print("ruff ");
numOfBarks--;
}
}
}
public static void main(String[] args) {
Dog d1=new Dog();
d1.name="Mike";
d1.age=5;
d1.bark(3);
}//ruff ruff ruff
可以传入一个以上的参数,也可以将变量当做参数传入。但要求类型符合。
Java是通过值传递的,也就是说通过拷贝传递。方法无法改变调用方所传入的参数。
封装(Encapsulation)
基本原则:将你的实例变量标记为私有的,并提供公有的getter和setter来控制存取动作。将实例变量标记为private
、 将getter与setter标记为public
封装类
public class GoodDog {
private int size;
public void setSize(int s){
size=s;
}
public int getSize(){
return size;
}
void bark(){
if(size>60){
System.out.println("Wooof! Wooof!");
}
else if (size>14){
System.out.println("Ruff! Ruff!");
}
else{
System.out.println("Yip! Yip!");
}
}
}
测试类
public class GoodDogTestDrive {
public static void main(String[] args){
GoodDog dog1=new GoodDog();
dog1.setSize(66);
GoodDog dog2=new GoodDog();
dog2.setSize(20);
System.out.println("dog1:"+dog1.getSize());
dog1.bark();
System.out.println("dog2:"+dog2.getSize());
dog2.bark();
}
}
/*
dog1:66
Wooof! Wooof!
dog2:20
Ruff! Ruff!*/
任何有值可以被运用到的地方,都可以用调用方法的方式来取得该类型的值
实例变量永远都会有默认值。如果没有明确赋值给实例变量,或者没有调用setter,实例变量还是会有值。
实例变量与局部变量的区别:
- 实例变量是声明在类中而不是方法中
- 局部变量是声明在方法中的
- 局部变量在使用前必须初始化,它没有默认值。
使用==
来比较两个primitive主数据类型,或者判断两个引用是否引用的是同一个对象。
使用equals()
来判断两个对象是否在意义上相等
五、编写程序
开发真正的"Sink a DotCom"游戏:
游戏目标:以最少的猜测次数打掉计算机所安排的达康公司(Dot Com)网站。计算机会根据你的表现进行评分。
初始设置:程序启动后,计算机会在7×7的方格上安排3个达康网站,游戏会要求你开始猜坐标。
进行游戏:计算机会提示你输入猜测的位置。例如"A3",“D5"等,计算机会回给你命中"Hit”、没中"Miss"、击沉"Sunk"等回应。当你清空所有的达康时,游戏会列出你的分数并结束。
六、认识Java的API
ArraryList 的操作
- 创建
ArrayList<Egg> myList=new ArrayList<Egg>();
// 创建出Egg类型的List。
//新的ArrayList对象会创建在堆上。
- 加入元素
Egg s=new Egg();
myList.add(s);
- 再加入元素
Egg b=new Egg();
myList.add(b);
- 查询大小
int theSize=myList.size();//2
- 查询特定元素
boolean isIn=myList.contains(b);//true
- 查询特定元素的位置
int idx=myList.indexOf(b);//1
- 判断集合是否为空
boolean empty=myList.isEmpty();//false
- 删除元素
myList.remove(s);
七、继承与多态
继承
在设计继承时,会把共同的程序代码放在某个类中,然后告诉其他的类说此类是它们的父。当某个类继承另一个类的时候,也就是子类继承自父类。
以Java的方式说,这是“子类继承父类”。继承的关系意味着子类继承了父类的方法。当我们提及“类的成员”时,成员的意思就是实例变量和方法。
extends
Animal类(父类)
public class Animal {
String food;//动物吃的食物
int age;//动物的年龄
String kind;//动物的种类
public void makeNoise(){};//关于动物发出声音的方法
public void eat(){};//关于动物进食的方法
public void sleep(){};//关于动物睡觉的方法
}
Dog类(子类)
class Dog extends Animal{
String clolor;//毛发的颜色
public void makeNoise(){
System.out.println("汪汪");
};//覆盖 。。犬吠
}
Dog d=new Dog();
d.kind="犬科";
d.age=15;
d.clolor="白色";
d.food="meat";
d.makeNoise();
}
设计继承树:
- 找出具有共同属性和行为的对象。。。。。。用继承来防止子类中出现重复的程序代码。
- 设计代表共同状态与行为的类。
- 决定子类是否需要让某项行为(也就是方法的实现)有特定不同的运作方式。
- 通过寻找使用共同行为的子类来找出更多抽象化的机会。
- 完成类的继承层次。
当你调用对象引用的方法时,你会调用到与该对象类型最为接近的方法。最低阶的会胜出。
public
类型的成员会被继承。
private
类型的成员不会被继承。
继承下来的方法可以被覆盖掉,但实例变量不能被覆盖掉。
遵守合约:覆盖的规则
- 参数必须要相同,且返回类型必须要兼容
- 不能降低方法的存取权限
存取权限必须相同或者更为开放。
比如,这个合约表示“我没有参数并且返回布尔值”,所覆盖的方法就必须没有参数并且返回布尔值。
多态
在多态下,引用和对象可以是不同的类型。
Animal myDog=new Dog();//引用变量的类型被声明为Animal,但对象是Dog
引用的类型(Animal)可以是实际对象类型(Dog)的父型。
方法的参数和返回的类型也可以多态。
class Dog extends Animal{};
class Vet {
public void giveShot(Animal a){
a.makeNoise();
}
};//a的参数可以用任何Animal的类型对象来传入。
class petOwner{
public void start(){
Vet v=new Vet();
Dog d=new Dog();
v.giveShot(d);
}
方法的重载。。。。。。与多态、覆盖毫无关系
重载可以有同一方法的多个不同参数版本以方便调用。
重载方法不是用来满足定义在父类的多态合约的,所以重载的方法比较有扩展性。
重载的条件是使用不同的参数
- 返回的类型可以不同
- 不能只改变返回类型(需要改变参数)
- 可以更改存取权限
//重载
public class Overloads{
String uniqueId;
public int addNums(int a,int b){
return a+b;
}
public double addNums(double a,double b){
return a+b;
}
public void setUniqueID(String theID){
uniqueId=theID;
}
public void setUniqueID(int ssNumber){
String numString=""+ssNumber;
setUniqueID(numString);
}
}
八、接口与抽象类
可以有Animal aWolf=new Wolf();
有些类不应当被初始化。列如Animal anim=new Animal();
所以需要设计抽象的类:在类声明的前面加上抽象类关键词abstract
abstract class Canine extends Animal{
public void roam(){}}
编译器是不会初始化抽象类的。
抽象类除了被继承过外,是没有用途、没有值、没有目的的。
同样也可以将方法抽象
public abstract void eat();
抽象的方法没有实体,直接以分号结束。抽象的方法必须被覆盖过之后才能在子类中使用。
声明一个抽象的方法时,此类也必须标记为抽象。
抽象方法的意义是就算无法实现出方法的内容,但还是可以定义出一组子型共同的协议。它没有内容,只是为了标记出多态而存在。
在Java中的所有类都是从Object这个类继承出来的。因而ArraryList可以处理任何类。
编译器是根据引用类型来判断有哪些method可以调用,而不是Object类型。
interface关键词——接口,解决多重继承的问题却又不产生致命方块的问题。
接口的定义
public interface Pet{
public abstract void beFriendly();
public abstract void play();//接口的方法一定是抽象的,所以必须以分号结束。他们没有内容
}//使用interface替代class
接口的使用方法
public class Dog extends Animal implements Pet{//关键词 implements
public void beFriendly(){
}
public void play(){
}//必须在这里实现抽象类的覆盖
public void eat(){
}//一般的覆盖方法
}
类可以extend过某个父类,并且实现其他的接口。同时其他的类也可以实现同一个接口。因此你就可以为不同的需求组合出不同的继承层次。
类可以继承多个接口
public class Dog extends Animal implements Pet,Saveble,paintable{}
调用父类的方法:
abstract class Report{
void runReport(){
//设置报告
}
void printReport(){
//输出
}
}
class BuzzwordReport extends Report{
void runReport(){
super.runReport();//调用父版的方法
buzzwordCompliance(){};//其他方法
}
}
可以创建出一个具体的子类,不打算完全覆盖掉原来的方法,只是要加入额外的动作。
九、构造器与垃圾收集器
栈:方法的调用和局部变量
堆:所有的对象
实例变量:声明在类而不是方法里,代表每个独立对象的“字段”,存在于所属的对象中。存在于对象所属的堆空间上。
public class Duck{
int size
}//size就是一个实例变量
局部变量:局部变量和方法的参数被声明在方法中,他们是暂时的。且生命周期只限于方法被放在栈上的时间(也就是方法调用至执行完毕为止)
public void foo(int x){
int i=x+3;
boolean b=true;
}//参数x和变量i、b都是局部变量
栈顶上的方法是目前正在执行的方法。
对象引用变量与primitive主数据类型变量都放在栈上。
不论对象在哪里声明,它总是运行在堆上。
Dog myDog = new Dog();
我们调用了Dog的构造函数。
构造函数带有你初始化对象时会执行的程序代码,也就是新建一个对象是就会被执行。它使得我们有机会介入new的过程。
public Dog{
}//构造函数没有返回值类型
先构造对象,再进行初始化时很危险的,因为用户可能不会去执行setSize()方法。当我们需要初始化时,可以自己编写构造函数。
最好的方法是 把初始化的程序代码放在构造函数中,然后把构造函数设定成需要参数的。
public class Dog2 {
int size;
public Dog2() {
//指定默认值
size = 14;
}
public Dog2(int dogSize) {
//使用参数设定
size = dogSize;
}
}
//知道大小时:
Dog2 d=new Dog2(19);
//不知道大小时:
Dog2 d2=new Dog2();
重载构造函数的意思代表你有一个以上的构造函数且参数都不相同。
- 构造函数是在新建类时会执行的程序。
Duck d = new Duck();
- 构造函数必须与类的名字一样,且没有返回类型。
public Duck (int size){}
- 如果你没有写构造函数,则编译器会帮你写一个没有参数的。
public Duck(){}
- 一个类可以有很多个构造函数,但不能有相同的参数类型和顺序,这叫做重载过的构造函数。
public Duck(){}
public Duck(int size){}
public Duck(String name){}
public Duck(String name,int size){}
在创建新对象时,所有继承下来的构造函数都会执行。
调用父类构造函数的唯一方法是调用super()
public class Duck extends Animal{
int size;
public Duck (int newSize){
super();//合法调用
size=newSize;
}
编译器会帮我们加上super()
的两种情况:
- 没有编写构造函数
- 有构造函数但是没有调用
super()
对super()
的调用必须是构造函数的第一个语句。
public class Dog extends Animal{
public Dog(){
super();
}
}
public class Dog extends Animal{
int size;
public Dog(int i){
super();
size=i;
}
}
public class Dog extends Animal{
int size;
public Dog(int i){
size=i;
}
}
有参数的父类构造函数
Animal.java
public abstract class Animal {
private String name;//每个Animal都会有名字
public String getName(){//子类会继承这个getter
return name;
}
public Animal(String theName){
name=theName;//有参数的构造函数,用来设定name
}
}
Hippo.java
public class Hippo extends Animal {
public Hippo(String name) {//这个构造函数会要求传入name
super(name);//传给Animal的构造函数
}
}
MakeHippo.java
public class MakeHippo {
public static void main(String[] args) {
Hippo h = new Hippo("Buffy");
System.out.println(h.getName());
}
}
//Buffy
对象的生命周期
实例变量的寿命与对象相同。如果对象还活着,则实例变量也会是活的。
局部变量只会存活在声明该变量的方法中。
public class Life{
int size;
public void setSize(int s){
size=s;
//'s'会在方法结束时消失,、
//但size在类中,到处可用
}
}
public void read(){
int s;
//'s'只能存在于此方法中,当方法完全结束后,s会完全消失
}
当最后一个引用消失时,对象就会变成可回收的。
- 引用永久性离开了它的范围
void go(){
Life z=new Life(2);//x在方法结束时消失
}
- 引用被赋值到其他的对象上
Life z = new Life(2);
Life k = new Life(4);
z = k;//第一个对象会在z被赋值到别处时挂掉。
- 直接将引用设定为null
Life z = new Life(2);
z=null;
十、数字与静态
Math方法:最接近全局的方法。它不需要实例变量。因为他们的方法都是静态的。
int x=Math.abs(-90);
int y=Math.min(56,29);
System.out.println("x="+x);//x=90
System.out.println("y="+y);//y=29
static
关键词可以标记出不需要类实例的方法。
一个静态的方法代表说“一种不依靠实例变量也就不需要对象的行为”。
- 以类的名称调用静态的方法。
Math.min(88,34);
- 以引用变量的名称调用非静态的方法。
Dog d=new Dog;
d.paly();
静态的变量无法调用非静态的变量,也无法调用非静态的方法。
静态变量——它的值对所有的实例来说是相同的。它是被同类的所有实例共享的变量。
- 实例变量:每个 实例 一个
- 静态变量:每个 类 一个
Duck.java
public class Duck {
private int size;
private static int duckCount=0;
public Duck(){
duckCount++;//该构造函数执行时,此变量的值会增加
}
public void setSize(int s){
size=s;
}
public int getSize(){
return size;
}
public int getDuckCount(){
return duckCount;
}
}
TestDuck.java
public static void main(String[] args){
Duck d1=new Duck();
Duck d2=new Duck();
d1.setSize(3);
d2.setSize(4);
System.out.println(d1.getDuckCount());//2
}
静态变量会在该类的任何静态方法执行前初始化。
System.out.println(Duck.duckCount);
可以通过类名来存取静态变量。但前提是它不是private
的
final
的变量是常数,无法改变它的值。且静态的变量常数的名称必须是大写字母。
final
的方法无法被覆盖。
final
的类无法被继承。
public static final double PI = 3.1415
public class Foo{
public static final FOO_X=5;
static{
FOO_Y=8;}
}
final int size=3;
final void play(){}
final class Dog{}
autoboxing:不必把primitive主数据类型与对象分得那么清楚
ArrayList<Integer> listOfNumbers=new ArrayList<Integer>();
listOfNumbers.add(3);
ArrayList<int> listOfNumbers=new ArrayList<int>();//报错,应当是ArrayList<Integer>
ArrayList<Boolean>
ArrayList<Character>
ArrayList<Byte>
ArrayList<Short>
ArrayList<Integer>
ArrayList<Long>
ArrayList<Float>
ArrayList<Double>
格式化结构
String s=String.format("%,.2f",21323133.1332);
System.out.println(s);//21,323,133.13
int a=36278;
double b=21223123.32121;
String s=String.format("The rank is %,d out of %.3f",a,b);
System.out.println(s);//The rank is 36,278 out of 21,223,123.321
Date today=new Date();
System.out.println(today);//Sun Mar 21 21:24:15 CST 2021
完整的日期与时间:%tc
String s=String.format("%tc",new Date());
//周日 3月 21 21:18:28 CST 2021
只有时间:%tr
String s=String.format("%tr",new Date());
//09:21:02 下午
周、月、日:%tA%tB%td
Date today=new Date();
String s= String.format("%tA %tB %td",today,today,today );
//星期日 三月 21
要取得当前的时间可以用Date,其余功能可以从Calendar上找。
Calendar cal=Calendar.getInstance();//对静态方法的调用
cal.set(2021,1,26);//设定时间
System.out.println(cal.getTime());//Fri Feb 26 21:36:34 CST 2021
cal.add(cal.DATE,36);//给cal的时间加上36天
System.out.println(cal.getTime());//Sat Apr 03 21:37:30 CST 2021
cal.set(cal.DATE,5);//直接设定DATE的值
System.out.println(cal.get(cal.DATE));//5 取出指定字段的值,此处是DATE
cal.roll(cal.DATE,32);// 加减时间,但是不进位
System.out.println(cal.getTime());//Wed Apr 07 21:41:41 CST 2021
cal.set(cal.MONTH,4);//设定指定字段的值
System.out.println(cal.getTime());//Fri May 07 21:43:42 CST 2021
十一、异常处理
异常是一种Exception类型的对象
try {
//危险动作
} catch (Exception ex) {
// try 的内容执行成功时,不会运行catch部分。
// try内容一旦发生错误就立即停止并运行catch部分
//尝试恢复
}finally {
//无论如何都要执行的部分
//如果try和catch有return指令,还是会要执行finally然后再回到return指令。
}
try{}
与catch{}
之间不能有程序
public class Laundry{
public void doLaundry() throws Exception{//声明该方法可能会抛出异常
//可能会抛出异常的程序代码
}
}
public class Test{
public void go(){
Laundry L=new Laundry();
try{//调用可能出现异常的方法
L.doLaundry();
}catch(Exception ex){
}finally{
}
}
}
当有多个异常需要处理时,可以设置多个catch
块,但是处理异常的范围要从下到大
我们可以只声明方法可能存在异常throw Exception
而不去设置try{}catch{}
模块去处理它,这样会把异常duck掉,把异常留给调用方。
十二、图形用户接口
JFrame是代表屏幕上window的对象。你可以把button、checkbox、text字段等接口放在window上面。
import javax.swing.*;
public class SimpleGui1 {
public static void main(String[] args){
JFrame frame=new JFrame();//创建frame
JButton button=new JButton("Click me");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//该代码会在window关闭时把程序结束掉
frame.getContentPane().add(button);//把button加到frame上
frame.setSize(300,300);//设定frame的大小
frame.setVisible(true);//最后把frame显示出来
}
}
监听接口是介于监听(你)与事件源(按钮)间的桥梁。
事件源(例如按钮)会在用户做出相关动作时(按下按钮)产生事件对象。
实现监听接口让按钮有一个回头调用程序的方法。interface正是声明调用(call-back)方法的地方。
取得按钮的ActionEvent:
- 实现ActionListener这个接口。
- 向按钮注册(告诉它你要监听事件)。
- 定义事件处理的方法(实现接口上的方法)。
import javax.swing.*;
import java.awt.event.*;
public class SimpleGui1 implements ActionListener{//实现此接口。这表示SimpleGui1是一个ActionListener
JButton button;
public static void main(String[] args) {
SimpleGui1 gui=new SimpleGui1();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
button = new JButton("Click me");
button.addActionListener(this);//向按钮注册
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){//实现interface上的方法,按钮会以ActionEvent对象作为参数来调用此方法。
button.setText("OK");
}
}
点击之后
内部类可以帮助我们对两个不同的按钮分别取得事件。
public class MyOuterClass {
private int x;//外部有个私用的x实例变量
MyInnerClass inner=new MyInnerClass();//创建内部的实例
public void doStuff(){
inner.go();//调用内部的方法
}
class MyInnerClass {
void go() {
x = 20;//内部可以使用外部的x变量
}
}
}
内部类可以使用外部类的所有方法与变量,即使他们是私用的。
package pers.cc.Demo;
//将右侧的标签名字先后改成1和2
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TwoButtons {
JFrame frame;
JLabel label;
public static void main(String[] args){
TwoButtons gui=new TwoButtons();
gui.go();
}
public void go(){
frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton labelButton=new JButton("Change Label1");
labelButton.addActionListener(new LabelListener());//传的是对应的实例
JButton colorButton=new JButton("Change Label2");
colorButton.addActionListener(new ColorListener());//传的是对应的实例
label=new JLabel("I am a label");
frame.getContentPane().add(BorderLayout.SOUTH,colorButton);//BorderLayout.设置位置
frame.getContentPane().add(BorderLayout.CENTER,labelButton);
frame.getContentPane().add(BorderLayout.EAST,label);
frame.setSize(300,300);
frame.setVisible(true);
}
//可以在单一的类中做出不同的ActionListener
class LabelListener implements ActionListener {//内部类
public void actionPerformed(ActionEvent event){
label.setText("1");
}
}
class ColorListener implements ActionListener {//内部类
public void actionPerformed(ActionEvent event){
label.setText("2");
}
}
}
十三、运用Swing
布局管理器会控制嵌套在其他组件中组件的大小和位置。
FlowLayout布局组件的流向:依次从左至右、从上至下(面板默认使用)
package pers.cc.Demo;
import javax.swing.*;
import java.awt.*;
public class FlowLayout_Panel {
public static void main(String[] args){
FlowLayout_Panel gui=new FlowLayout_Panel();
gui.go();
}
public void go(){
JFrame frame=new JFrame();
JPanel panel=new JPanel();//生成面板
JButton button1=new JButton("button1111111");
JButton button2=new JButton("button2222");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setBackground(Color.darkGray);//让面板变为深灰色以便观察
panel.add(button1);//把按钮添加到面板上
panel.add(button2);
frame.getContentPane().add(BorderLayout.EAST,panel);
frame.setLocation(300,300);
frame.setSize(300,300);
frame.setVisible(true);
}
}
BoxLayout布局,就算水平宽度足够,也还是会用新的行来排列组件。(框架默认使用)
package pers.cc.Demo;
import javax.swing.*;
import java.awt.*;
public class FlowLayout_Panel {
public static void main(String[] args){
FlowLayout_Panel gui=new FlowLayout_Panel();
gui.go();
}
public void go(){
JFrame frame=new JFrame();
JPanel panel=new JPanel();
JButton button1=new JButton("button1111111");
JButton button2=new JButton("button2222");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setBackground(Color.darkGray);//让面板变为深灰色以便观察
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));//把布局管理器替换掉
//BoxLayout的构造函数需要知道管理哪个组件以及使用哪个轴
panel.add(button1);//把按钮添加到面板上
panel.add(button2);
frame.getContentPane().add(BorderLayout.EAST,panel);
frame.setLocation(300,300);
frame.setSize(300,300);
frame.setVisible(true);
}
}
几个常用的组件
- text field
//构造函数
JTextField filed = new JTextField("Your name", 20);//20代表20字宽而不是像素
//取得文本内容
System.out.println(filed.getText());
//设定内容
filed.setText("Hello");
filed.setText("");//清空字段
//取得用户输入完毕按下return或enter键的事件
filed.addActionListener(myActionListener);
//选取文本字段内容
filed.selectAll();
//把GUI目前焦点拉回到文本字段以便让用户进行输入操作
filed.requestFocus();
- 可滚动的text area
//构造函数
JTextArea text=new JTextArea(10,20);//10行高、20字宽
//只有垂直的滚动条
JScrollPane scroller=new JScrollPane(text);//将text赋值给新创建的JScrollPAne
text.setLineWrap(true);//启动自动换行
scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);//指定只使用垂直滚动条
panel.add(scroller);//加入的是带有文本域的滚动条而不是文本域。
//替换掉文字内容
text.setText("Hello word");
//加入文字
text.append("Are you ok?");
//选取内容
text.selectAll();
//把GUI目前焦点拉回到文本字段以便让用户进行输入操作
text.requestFocus();
- checkbox
//构造函数
JCheckBox check=new JCheckBox("text1");
//监听item事件
check.addItemListener(this);
//处理事件(判断是否被选取)
public void itemStateChanged(ItemEvent ev){
String onOrOff="off";
if(check.isSelected()) onOrOff="on";
System.out.println("Check box is "+onOrOff);
}
//用程序来选取或者不选取
check.setSelected(true);
check.setSelected(false);
十四、系列化和文件的输入/输出
如果只有自己写的Java程序会用到这些数据:序列化。
如果数据需要被其他程序引用:写一个纯文本文件。用其他程序可以解析的特殊字符写到文件中。例如写成用Tap字符来分隔的档案以便让电子表格或者数据库应用程序能够应用。
鉴于数据操作与数据库接触频繁,此处不做详细记录。
十五、网络与线程
Socket连接的建立代表两台机器之间存有对方的信息,包括网络地址和TCP的端口号。
客户端与服务器的应用程序通过Socket连接来沟通
Socket chatSocket=new Socket("127.0.0.1",5000);
//127.0.0.1 是IP地址,这个IP地址比较特殊,就是“本机”.5000是端口号,其范围一般是1024-65535.
客户端
使用BufferedReader从Socket上读取数据
//建立对服务器的Socket连接
Socket chatSocket=new Socket("127.0.0.1",5000);
//建立连接到Socket上底层输入串流的InputStreamReader,InputStreamReader是底层和高层之间的桥梁
InputStreamReader stream=new InputStreamReader(chatSocket.getInputStream());//从Socket取得输入串流
//建立BufferedReader来读取
BufferedReader reader=new BufferedReader(stream);//stream将ButteredReader连接到InputStreamReader
String message=reader.readLine();
用PrintWriter写数据到Socket上
//对服务器建立Socket连接
Socket chatSocket=new Socket("127.0.0.1",5000);
//建立连接到Socket的PrintWriter,PrintWriter是字符数据和字节间的转换桥梁,可以衔接String和Socket两端
PrintWriter writer=new PrintWriter(chatSocket.getOutputStream());
//写入数据
writer.println("message to send");
writer.print("aother message");
服务器
一个简单的服务器程序
//服务器应用程序对特定端口创建出ServerSocket
ServerSocket serverSocket=new ServerSocket(4242);//让服务器应用程序开始监听来自4242端口的客户端请求。
//客户端对服务器应用程序建立Socket连接
Socket socket=new Socket("127.0.0.1",4242);//客户端得知道IP地址与端口号
//服务器创建出与客户端通信的新Socket
Socket sock=serverSocket.accept();//当用户连上来时,accept()会返回一个Socket以便让客户端通信。
线程
线程是独立的线程。它代表独立的执行空间。
Java中用Thread表示线程的类,要建立线程就得创建Thread。
Java虚拟机会负责启动自己建立的线程,程序员得负责启动自己建立的线程。
如何启动新的线程
//1.建立Runnable对象(线程的任务)
Runnable threadJob=new MyRunnable();
//2.建立Thread对象(执行工人)并赋值Runnable(任务)
Thread myThread=new Thread(threadJob);
//3.启动Thread
myThread.start();
Thread对象需要任务。任务是线程在启动时去执行的工作。该任务是新线程空间上的第一个方法,且一定如下:
public void run(){
//会被新线程执行的代码
}
Runnable这个接口只有一个方法:public void run()
当你把Runnable传给Thread的构造函数时,实际上就是在给Thread取run()的方法。这就等于你给了Thread一项任务。
实现Runnable接口来建立给thread运行的任务
/MyRunnable.java
public class MyRunnable implements Runnable{//调用Runnable接口
public void run(){//只有一个方法需要实现。要运行的程序放在这里
go();
}
public void go(){
doMore();
}
public void doMore(){
System.out.println("Hello word");
}
}
//ThreadTest.java
public class ThreadTest {
public static void main(String[] args){
Runnable threadJob=new MyRunnable();
Thread myThread=new Thread(threadJob);//将Runnable的实例传给Thread的构造函数
myThread.start();
System.out.println("back to main");
}
}
//有时候主线程会先结束,有时候新建进程会先结束。
一旦线程进入可执行状态,它会在可执行与执行中两种状态中来来去去,同时也有另一种状态:暂时不可执行(又被称为堵塞状态)
线程调度器会做所有的决定。谁跑谁停都要看它。它通常是公平的。但没有人能保证这件事,有时候某些线程很受宠,有些进程还会被冷落。
调用sleep()来强迫此线程离开执行中的状态。
try{
Thread.sleep(2000);
}catch (InterruptedException ex){
ex.printStackTrace();
}
并发性问题——两个或以上的线程存取单一对象的数据。也就是说两个不同执行空间上的方法都在堆上对同一个对象执行getter或setter。
使用synchronized
这个关键词来修饰方法使它每次只能被单一的线程存取。该关键词代表线程需要一把钥匙来存取被同步化过的线程。要保护数据,就把作用在数据上的方法给同步化。
同时,它还能会让方法中的两个步骤组成不可分割的单元。一旦线程进入了方法,我们必须确保在其他线程可以进入该方法之前所有的步骤都会完成(如原子不可分割一样)。
如果对象有同步化的方法,则线程只能在取得钥匙的情况下进入线程。也就是说并没有其他线程已经进入的情况下才能进入。
public synchronized void increment(){
int i=balance;
balance=i++;
}
原则上最好只做最少量的同步化:
public void go(){//不需要整个都同步化
F0();
synchronized (this) {
//只有这两个调用要被组合成原子单位,同步化的这种用法不会将整个方法设定成需要同步化,只会使用参数所指定对象的锁来做同步化。
F1();
F2();
}
}
死锁——两个线程互相持有对方正在等待的东西,没有方法可以脱离这个情况,所有两个线程只好停下来等。