Java学习笔记

一、基本概念

 先创建然后再建立相应的类最后建立主方法
 用 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的两种用途:

  1. 测试真正的类
  2. 启动你的Java程序

 真正的Java程序只会让对象与对象进行交互(相互调用方法)。
 不要总是呆在main()中

猜数字游戏

摘要:
 这个游戏涉及到game和player两个对象。game会产生0-9之间的随机数字,而3个player对象会猜测该数字

类:

  • GuessGame.class
  • Player.class
  • GameLauncher.class

程序逻辑:

  1. GameLauncher这个类带有main()方法,是程序的入口。
  2. main()中会创建出GuessGame对象,并调用它的starGame()方法
  3. 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"等回应。当你清空所有的达康时,游戏会列出你的分数并结束。

DotComBust 创建DotCom、 取得玩家的输入、 进行游戏 DotCom DotCom对象有名称、位置、 知道如何判别玩家是否命中 GameHelper 预先写好的Ready—Bake程序

代码

六、认识Java的API

ArraryList 的操作

  1. 创建
 ArrayList<Egg> myList=new ArrayList<Egg>();
// 创建出Egg类型的List。
//新的ArrayList对象会创建在堆上。
  1. 加入元素
  Egg s=new Egg();
  myList.add(s);
  1. 再加入元素
 Egg b=new Egg();
 myList.add(b);
  1. 查询大小
    int theSize=myList.size();//2
  1. 查询特定元素
boolean isIn=myList.contains(b);//true
  1. 查询特定元素的位置
int idx=myList.indexOf(b);//1
  1. 判断集合是否为空
boolean empty=myList.isEmpty();//false
  1. 删除元素
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();
    }

设计继承树:

  1. 找出具有共同属性和行为的对象。。。。。。用继承来防止子类中出现重复的程序代码。
  2. 设计代表共同状态与行为的类。
  3. 决定子类是否需要让某项行为(也就是方法的实现)有特定不同的运作方式。
  4. 通过寻找使用共同行为的子类来找出更多抽象化的机会。
  5. 完成类的继承层次。

  当你调用对象引用的方法时,你会调用到与该对象类型最为接近的方法。最低阶的会胜出。
在这里插入图片描述

public类型的成员被继承。
private类型的成员不会被继承。
继承下来的方法可以被覆盖掉,但实例变量不能被覆盖掉。

遵守合约:覆盖的规则

  1. 参数必须要相同,且返回类型必须要兼容
  2. 不能降低方法的存取权限
    存取权限必须相同或者更为开放。

 比如,这个合约表示“我没有参数并且返回布尔值”,所覆盖的方法就必须没有参数并且返回布尔值。

多态

 在多态下,引用和对象可以是不同的类型。

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();

 重载构造函数的意思代表你有一个以上的构造函数且参数都不相同。

  1. 构造函数是在新建类时会执行的程序。
Duck d = new Duck();
  1. 构造函数必须与类的名字一样,且没有返回类型。
public Duck (int size){}
  1. 如果你没有写构造函数,则编译器会帮你写一个没有参数的。
public Duck(){}
  1. 一个类可以有很多个构造函数,但不能有相同的参数类型和顺序,这叫做重载过的构造函数。
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()的两种情况:

  1. 没有编写构造函数
  2. 有构造函数但是没有调用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会完全消失
    }

当最后一个引用消失时,对象就会变成可回收的。

  1. 引用永久性离开了它的范围
 void go(){
        Life z=new Life(2);//x在方法结束时消失
    }
  1. 引用被赋值到其他的对象上
       Life z = new Life(2);
       Life k = new Life(4);
             z = k;//第一个对象会在z被赋值到别处时挂掉。
        
  1. 直接将引用设定为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:

  1. 实现ActionListener这个接口。
  2. 向按钮注册(告诉它你要监听事件)。
  3. 定义事件处理的方法(实现接口上的方法)。
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();

JTextArea范例

  • 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.

客户端

 使用BufferedReaderSocket上读取数据

//建立对服务器的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();
           }
        }

死锁——两个线程互相持有对方正在等待的东西,没有方法可以脱离这个情况,所有两个线程只好停下来等。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值