Java课程学习总结

目录

0 Java语言概述

0.1 Java的特点

  • 面向对象,Java程序“一切都在类中”,支持继承、多态机制

  • 语法来自C++,取消了指针,多重继承和运算符重载,设立了自动内存回收机制,提供丰富类库

  • 跨平台,一遍编译,到处执行

  • Java程序具有半编译,半解释的特性,由编译器将源程序编程成字节码文件,再由JVM将字节码文件解释执行。比解释性语言的执行效率高,比编译型语言更具灵活性(可移植)

  • 支持分布式(数据分布和操作分布)

  • 引入多级安全措施(内存使用、字节码验证器)

  • 支持多线程

0.2 Java体系结构

虚拟机原理

C++ 和 Java 这两种编译型语言都是跨平台的,只是它们跨平台的层次不同而已: 前者是源码层(Fortran 和 Pascal 也是),后者是可执行文件层。 也就是说,要移植的话,C++ 程序必须在目的平台上重新编译,Java 则只需把可执行文件拷到目的平台上去,不必在那里重新编译。此处 “可执行文件” 的定义是 “编译器的输出文件”(C++ 的是 .exe,Java 的是 .class)。

当编译C或者C++,所获取的二进制文件只能适合指定的硬件平台和操作平台的(这个二进制文件包含了一些机器码,而且字节顺序还依赖特定平台,比如高位在前,低位在前等),而编译java形成的是java class文件,而class文件适合任何支持Java虚拟机的硬件平台和操作系统上的二进制的文件。

  • class文件是Java源程序编译后得到的文件,也称作字节码文件,它是在Java虚拟机之上运行的目标代码。

  • Java字节码文件可以在任何安装了Java虚拟机的平台上运行。

  • Java虚拟机是实现平台无关性的关键,位于Java程序和用户的计算机系统之间。它与具体的软硬件平台有关,从而保证了在它上运行的Java字节码与具体软硬件平台是无关的。

Java API

Java API(Application Program Interface,应用程序接口)是运行库的集合,它是已编译好的的程序代码库,可以直接使用。它提供了一套访问主机系统资源的标准方法。

Java API的class文件是与主机平台密切相关的。由于在程序执行的时候Java API调用了本地方法,Java源程序就不用调用他们了,通过这种方法,Java API 的class文件为底层提供了具有平台无关性,标准接口的Java程序。这样对于Java程序而言,无论平台内部如何,Java API都会有同样的表现和预测行为。正是由于在每个特定的平台都明确实现了Java虚拟机和Java API,才或有Java的无关性。

Java开发执行流程

关于编码:Java的字符使用Unicode编码,使用两个字节(16位),所以它几乎包括所有字符,中文、日文、…,它支持世界上所有语言。

大多数语言使用ASCII码,用8位表示一个字符。ASCII码是Unicode码的一个子集,Unicode表示ASCII码时,其高位为0,它是其前255个字符。Unicode字符通常用十六进制。例如“\u0000”-“\u00ff” 表示ASCII码集。”\u”表示转义字符,它用来表示其后四个十六进制数字是Unicode代码。

老师还讲到,我们在写代码时,用到的编码是我们电脑自带的编码格式,一般是GBK,只有编译后也就是生成字节码文件后才成为Unicode编码。

1 关于Java安装

1.1 JRE还是JDK?

JRE(JavaRuntimeEnvironment)包括Java虚拟机(JVM)和 Java API,如果只需要运行Java程序或Applet,下载并安装它即可。如果要自行开发 Java软件,就需要下载JDK,在JDK中附带有JRE。

可以到官网下载,不用下载最新版本,一般用JDK8的比较多。

1.2 JDK文件说明

bin 一些可执行文件和动态链接库,如Javac、Java、javah等运行程序
demo Java程序实例文件
include 固有方法(Native Methods)专用文件
jre Java运行时环境相关的文件
lib Java的库文件
src.zip Java函数库的源代码
doc 存放以HTML格式的Java的库参考和帮助文档

1.3 配置环境变量

操作系统的环境变量是指在操作系统中定义的变量,它可以被所有运行在该操作系统中的程序所访问。

系统变量对所有操作系统用户都有效,用户变量:只对该用户有效。某个用户所能使用的环境变量=该用户变量+系统变量。

当我们在命令行界面中执行某个命令时,操作系统首先在当前目录下查找,如果没有,则操作系统将在Path所指定的目录下依次查找,以最先找到的那个为准。

我们需要在 "系统变量" 中设置 3 项属性,JAVA_HOME、PATH、CLASSPATH(大小写无所谓),若已存在则点击"编辑",不存在则点击"新建"。1.5 以上版本的 JDK,不用设置 CLASSPATH 环境变量,也可以正常编译和运行 Java 程序。JDK8也就是1.8版本不用设置CLASSPATH。

变量名 变量值
JAVA_HOME D:\Development\Java\JDK8 //以自己JDK的安装路径为准
Path %JAVA_HOME%\bin
Path %JAVA_HOME%\jre\bin
CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;

可能会见到这样配置Path变量的:

Path  %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;

好像这是Win7这样的老机子上的写法。在 Windows10 中,Path 变量里是分条显示的,所以用上面表格里的方式就行了。

关于CLASSPATH变量:

CLASSPATH变量值,前面有个"."表示还会在当前目录下查找该类,不加的话只在CLASSPATH所指定的路径中查找该类并运行。

CLASSPATH的作用:当用java命令运行一个class文件时,系统要通过CLASSPATH找到对应的字节码文件;或者在要运行程序中还用到了第三方的类库(jar),系统也要通过CLASSPATH找到这个类库。

下面是操作步骤供参考:

右键【此电脑】→【属性】,下拉找到并点击【高级系统设置】,然后配置【环境变量】

下图演示添加JAVA_HOME路径,需要新建一个系统变量。

变量名Path是系统已经建好的,我们只需要点击它,然后添加新的变量值就行了,添加两个变量值,分别是%JAVA_HOME%\bin%JAVA_HOME%\jre\bin

CLASSPATH变量就不添加了。

完成上述工作后,打开一个命令行窗口,输入java -version,如果显示你的java版本信息就说明安装成功了。

2 使用Java进行编程

2.1 记事本编写代码

在桌面新建一个文件夹,然后用记事本写一个文本文档命名为HelloWorld ,并将后缀名改为.java 。内容如下

public class HelloWorld
{
    public static void main(String[] args)
    {
         System.out.println("Hello World!");
    } 
}	

打开命令行,使用cd命令将路径切换到当前文件夹,先使用javac命令将HelloWorld.java翻译为字节码文件

javac HelloWorld.java

如果没有报错,会生成一个HelloWorld.class字节码文件,接着使用java命令来执行该文件(不要带.class后缀)

java HelloWorld

进展顺利的话命令行窗口就会输出Hello World!

2.2 集成开发环境

  • jetbrains全家桶系列IntelliJ IDEA ,功能强大。社区版免费,专业版在校大学生使用学校给的邮箱也可以申请免费使用。

  • 还有就是Eclipse,也很好用。

3 Java语言基础

很多东西都跟C++差不多,只有一些特殊的地方需要注意。

3.1 标识符、注释

定义标识符的规则

  1. 由字母、 数字、下划线、 $组成。不能由数字开头
  2. 不能是Java中的保留字(关键字)。
  3. 大小写敏感,长度无限制

三种注释形式

  1. // 单行注释。表示从此向后,直到行尾都是注释。
  2. /*……*/ 块注释。在/**/之间都是注释。
  3. /**……*/文档注释。所有在/***/之间的内容可以用来自动形成文档。
//元旦快乐
    
/*
元旦快乐
元旦快乐
元旦快乐
*/
     
/**
* 演示文档注释
* @author gylic
* 你可以包含一个或多个各种各样的@标签。
* 每一个@标签必须在一个新行的开始或者在一行的开始紧跟星号(*)。
* 多个相同类型的标签应该放成一组。
* 例如,如果你有三个@see标签,可以将它们一个接一个的放在一起。
* 使用javadoc工具可以可以识别特定标签,然后生成文档。
*/

3.2 变量和常量

变量的使用

变量的使用规则

Java的变量有两种:局部变量 类成员变量

变量必须先定义后使用。

局部变量在使用前必须给定初值,否则,将编译出错,而类成员变量无此要求。

变量的命名方法

程序员应该为变量取有意义的名称,以利于程序的阅读和理解。习惯上,变量名以小写字母开头,若一个变量名由超过一个单词所组成,则第一个单词之后的所有单词都以大写字母开头,以便于理解该变量名。(小驼峰命名法)。

变量的作用域

类成员变量作用域是整个类

类成员函数的参数作用域是该成员函数

局部变量作用域是该变量所在代码块

常量的使用

常量是指在程序运行过程中其值不变的量。分为数值常量和符号常量。

数值常量如下

整型常量:42、052、0x2A、0X2A //分别表示十进制、八进制、十六进制整数
实型常量:12.1f
布尔常量:true
字符常量:’x’
字符串类型常量:“Test” 
  • 采用十进制表示实型常量时,小数点的两侧都必须有数字, 缺一不可,如+123.和 .56都是不合法的。

  • 科学表示法: <尾数> E <阶码 >

    • 尾数必须有,但小数部分可无
    • 阶码必须有,必须是整数
    • 基数是10

符号常量

使用修饰符final可以定义符号常量。常量的值一旦确定不可更改。

final int a=10;
//a=20;
//错误用法,因为常量只能赋值一次(初始化)
System.out.println(a);

类静态成员常量只能在定义时初始化;方法中的常量(局部常量)可以在定义时初始化,也可以先定义,以后再初始化。

3.3 基本数据类型

八种基本数据类型

数据类型名 占用内存空间 数值范围 缺省值 说明
byte 1字节 (-128)—127 0 整型
short 2字节 (-32768)—32767 0 整型
int 4字节 (-2147483648)—2147483647 0 整型
long 8字节 (-9223372036854775808)—9223372036854775807 0 整型
float 4字节 (±3.4028347E+38)—(±1.40239846E-45) 0.0F 实型
double 8字节 (±1.79769313486231570E+308)—(±4.94065645841246544E-324) 0.0D 实型
char 2字节 \u0000—\uFFFF \u0000 字符型
boolean 1字节 true或false False 布尔型

注意

  • 缺省值就是默认的初值,只对成员变量有用.局部变量在使用前必须人为地给定初值。

  • 一个整型常量在机器中默认以int类型存储。整型常量后面加上后缀 L 或 l,在机器中以long类型存储。

  • 一个实型常量在机器中默认以double类型存储。实型常量后加后缀F或f在机器中以float类型存储。

  • 布尔型数据只有两个值:true和false。Java中不可将布尔类型看做整型值。

  • 字符类型用来表示单个字符,采用16位二进制Unicode 编码表示

  • 字符常量是用两个单引号括起来的一个字符

枚举类型 enum

语法格式

enum 枚举名{常量列表}

其中的“常量列表”是用逗号分割的字符序列,称为枚举类型的常量(其要符合标识符的规定),举例:

enum Season{spring, summer, autumn, winter}
Season  x = Season.spring;   
//定义一个枚举变量x,枚举变量x只能取枚举类型中的常量
//通过使用枚举名和“.”运算符获得枚举类型中的常量

动态类型var

动态类型var是Java10新特性,用它来定义局部变量。 var动态类型是编译器根据变量所赋的值来推断类型, 声明时必须初始化,不能用作方法参数。

3.4 表达式及运算符优先级

算术运算

整数

int a=10/0     //编译无错,运行报错,异常
int a=10%0     //运行报错,异常

浮点数

double a=10.0/0     //Infinity(正无穷大)
double a=-10.0/0   //-Infinity(负无穷大)
double a=0.0/0       //NaN
double a=0.0%0      //NaN

字符串运算

String 类型在“+”运算中,+表示连接符,其他类型先转换成字符串类型,再进行连接运算。

System.out.println(‘a’+1);
System.out.println(“”+’a’+1);
System.out.println(“12”+12);

移位运算

运算符 用法 说明
>> op1>>op2 将op1的二进制表示向右移op2位,左边填充符号位
<< op1<<op2 将op1的二进制表示向左移op2位,左边填充0
>>> op1>>>op2 将op1的二进制表示向右移op2位,左边填充0

注意:

  • 右移n位后的结果与除以2的n次方效果相同

  • 左移n位后的结果与乘以2的n次方效果相同

  • 无符号右移要慎重

  • 在进行移位之前,java系统首先把移的位数与被移位的位数求余数,然后移动这个位数。也就是说,整型向右移动 32 位结果是其本身。

三元运算

Java 中唯一的三元运算符,其格式如下:

变量 =  <布尔表达式> ? <表达式1> : <表达式2>

当<布尔表达式>为真时,变量的值为<表达式1>的值,否则为<表达式2>的值。

赋值运算

boolean型的只能赋给boolean型,其他七种类型如果能自动转换则可直接赋值,否则要进行强制类型转换

运算符优先级

( )>单目运算符>双目运算符>三目运算符>赋值运算符

双目:算术>关系>逻辑

大多数运算符结合性为从左至右,赋值运算符的结合性为从右至左。

3.5 类型转换

自动类型转换

运算过程中,Java自动把精度较低的类型转换为另一种精度较高的类型。

手动强制类型转换

在Java中直接将高精度的值赋给低精度的变量会导致编译出错。这时可用强制类型转换来解决。形式为:(类型名)表达式。强制类型转换可能造成信息的丢失 。布尔型与其它基本类型之间不能转换。

隐含强制类型转换

Java中允许把int类型的常量赋给byte、short变量时不需要强制类型转换,但是把int类型的变量赋给byte、short类型的变量时必须强制转换,否则会出错。

3.6 输入输出

输入

  1. 通过main(String[] args)中的形参
class test
{
   public static void main(String[] args)
   {
       System.out.println(args[0]);
       System.out.println(args[1]);
   }
}

>java test s1 s2
s1
s2
  1. System.in是字节流,作用是从标准输入读一个字节,常用的方法如下:
//从流中读取一个字节并将该字节作为整数返回,若没有数据则返回-1 
int read();

//从流中读取多个字节放到b中, 返回实际读取到的字节数
int read(byte b[]); 

//从流中读取最多len字节的数据, 放到数组b的下标off开始的单元中,返回读取到的字节数
int read(byte b[],int off,int len);

要将数字串转换成实数,则:

//转成单精度数
float    f=Float.parseFloat(str.trim());

//转成双精度数  
double  d=Double.parseDouble(str.trim());  

读取示例

//读取一个字符
char ch=(char)System.in.read();

//读取一个数字
byte buf[]=new byte[20];
System.in.read(buf);
int anInt=Integer.parseInt(str.trim());

//读一串字符
char c;
try
{
    do
    {  
        c=(char)System.in.read();  
        System.out.print(c);      
    }
    while(c!='\n');
}
catch(IOException e){  }                    
  1. 借助java.util.Scanner类完成输入
//使用next():
Scanner scan = new Scanner(System.in);
if (scan.hasNext()) 
{
    String str1 = scan.next();
    System.out.println("输入的数据为:" + str1);
 }
scan.close();

//使用nextLine():
Scanner scan = new Scanner(System.in);
if (scan.hasNextLine()) 
{
    String str2 = scan.nextLine();
    System.out.println("输入的数据为:" + str2);
}
scan.close();

两者区别
next():

  1. 一定要读取到有效字符后才可以结束输入。
  2. 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
  3. 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
  4. next() 不能得到带有空格的字符串。

nextLine():

  1. 以Enter为结束符,也就是说,nextLine()法返回的是输入回车之前的所有字符。
  2. 可以获得空白。

scanner的方法

输出

println(参数)向标准输出设备(显示器)打印一行文本并换行

System.out.println(“Example”);

print(参数)向标准输出设备(显示器)打印一行文本但不换行

System.out.print(“Input Name”);
int x=2; 
System.out.print(x);

3.7 控制语句

if else,switch case,break,continue,return这些语句就不啰嗦了。

for循环的特殊用法

for(声明循环变量:数组或集合)  
   {循环体 }

for语句翻译成对于循环变量依次取数组的每一个元素的值。

“声明循环变量” ,必须是变量声明,不可以使用已经声明过的变量。

集合是一个实现了Iterable接口的类对象。

例如:

public class Test {
   public static void main(String[] args){
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ){
         System.out.print( x );
         System.out.print(",");
      }
      System.out.print("\n");
      String [] names ={"James", "Larry", "Tom", "Lacy"};
      for( String name : names ) {
         System.out.print( name );
         System.out.print(",");
      }
   }
}

3.8 数组

一维数组

定义一个数组

int[] arr = new int[20];    //首选方法
int arr[] = new int[20];    //非首选方法,但也可以这样

使用new 创建数组之后,数组中的每个元素被自动初始化为0。

不能这样定义数组:

int a[5]; 

数组初始化

方法1

int a[]={1,2,3,4}

方法2

int b[]=new int[3];
b[0]=8;
b[1]=9;

方法3

int[] a=new int[]{1,2,3,4};

方法4

int[] a;
a=new int[]{1,2,3,4};

不能这样初始化数组:

int[] a;
a={1,2,3,4};

每个数组都有一个属性length来指明其长度

int x[]={1,2,3,4};
for(int i=0;i<x.length;i++)    
{ 
    System.out.println(x[i]);
}

二维数组

定义二维数组

Java中的多维数组实际上是数组的数组,不一定是规则的矩阵数组。

说明一个整型的二维数组如下:

int intArray[ ][ ];
int[ ][ ] intArray;

创建二维数组

  • 直接为每一维分配空间,如:
int a[ ][ ]=new int[2][3];

  • 从最高维开始,分别为每一维分配空间,如:
int b[ ][ ]=new int[2][ ]; 
b[0]=new int[3]; 
b[1]=new int[5];     

二维数组元素的初始化

直接对每个元素进行赋值

int[][] a=new int[3][2]:
a[0][0]=2;
a[0][1]=3;
a[1][0]=1;
a[1][1]=5;
a[2][0]=3;
a[2][1]=4;

在说明数组的同时进行初始化

int a[ ][ ]={{2,3},{1,5},{3,4}} 

数组排序

import java.util.Arrays;

Arrays.sort(x);

二分查找

System.out.println(java.util.Arrays.binarySearch(x,1));   
//二分查找,在数组x中查找1,找到则输出0 
//如果没找到,则会输出一个小于0的数

4 Java面向对象程序设计

那么问题来了

4.1 Java类的定义

类是具有相同特征的多个对象的模板,它封装了这些对象的所有共同特征,包括数据和能完成的操作(成员变量和方法)。类是对象的模板,对象是类的一个具体实例。

Java是一种纯对象化的语言,所有的应用程序代码都必须以类构成,即使是程序的开始执行点,也必须包含在某个类中。

Java中类的定义格式如下

[修饰符] class 类名 [extends父类名] [implements接口名列表]
{
          [成员变量说明]
          [成员方法说明]
          [构造方法说明]
          [静态初始化说明]
          //类体中说明都是可选的, 也没有先后顺序之分 
};

4.2 类首说明

  • 类的修饰符:public、abstract、final
    访问权限修饰符:缺省或public。缺省类只能被同一个包中的类访问,public类可被所有类访问。
    abstract:抽象类。抽象类不能被实例化,它可能包含未实现的方法。
    final:最终类。它修饰的类不能被继承,即不能有子类。
  • 类名
  • extends:继承(父类)。后面只能跟一个父类,因为java仅支持单继承。
  • implements:实现 (接口)。接口名可以有多个。

4.3 成员变量说明

定义在成员函数中的变量只是局部变量,不是成员变量。类的成员变量和类中的局部变量可以重名。

Java中成员变量说明形式如下:

[修饰符] 成员变量类型 成员变量名列表;
  1. 访问权限修饰符

访问权限修饰符:public protected private,用来限定变量的作用域。

权限大小 public >protected >缺省>private 。

在表格中:√表示允许访问,×表示禁止访问 。

访问修饰符缺省时,成员变量只能被同一包(package) 中的所有类访问,所以也称为包(package)变量。缺省访问修饰符实际是friendly修饰符,但因为friendly不是Java语言的关键字,所以friendly修饰符不能显式说明。

public修饰的成员变量可以被程序中的任何类所访问。由于public成员变量不受限制, 这易使类的对象引起不希望的修改,建议成员变量尽量不要使用public修饰符。

protected修饰的成员变量可以被本包及有继承关系的类自由访问。

private修饰的成员变量只能在同一个类中使用。这种方式通常是最为安全的。

关于访问权限修饰符的总结

  • 具有继承关系的子类可以继承父类的一些成员变量,即可以不创建对象就可以直接访问,如果是同一个包的子类可以继承到public 缺省和protected修饰的变量,如果是不同的包的子类就只能继承到public 和protected的;

  • 如果是其他类,不管是一个包还是不在一个包,都要创建该类的对象才能引用

  • 如果是main方法,不管是本类还是非本类,要访问实例变量都要创建对象,可以引申到其他所有的类方法中

  • 私有成员只能在本类中访问,如果在main方法中访问私有成员 必须创建对象

  1. static修饰符

static修饰的成员变量称为类变量(静态变量),类变量为该类的所有对象所共享,它的值不因类的对象不同而不同。可以通过类来访问静态成员变量(不创建对象就可以访问),也可以通过该类的对象访问静态成员变量。
形式:类名.成员变量/对象名.成员变量

不用static修饰的成员变量又叫对象变量(实例变量),它的值因具体对象实例的不同而不同

  1. final修饰符

final定义的成员变量叫最终变量——java中的常量,常量在说明以后就不能改变其值。

无论是实例变量,还是类变量,都可以被说明成常量。final修饰符和static修饰符并不冲突。

类常量:一个成员变量同时被static和final修饰,它在类定义的所有对象中都只有唯一的值,且无法改变。一定要在定义时就给定初始值。

对象常量:一个final成员变量,没有static修饰,不同的对象可以有不同的值。一定要给初始值,可以在定义变量时赋初始值,也可以在每一个构造函数中进行赋值。

4.4 成员方法说明

成员方法首部说明的形式如下:

[方法修饰符] 返回值类型   方法名([形参列表])[throws异常列表]
{//方法体开始
    实现方法的代码段
    return 表达式;
    //return;用于返回值类型为void
}//方法体结束

/*方法体也可以是一个分号“;”,表示无具体方法(方法还没有实现,即只是一个方法框架。
当且仅当方法的修饰符中有abstract或native时,方法才可无方法体。*/

方法的修饰符

  • 访问修饰符: 缺省、public、protected、private

  • 非访问修饰符: static、abstract、final、native、synchronized

static:修饰的方法为静态方法,又叫类方法,类方法是该类的所有对象共享的方法。无static修饰的方法为对象方法
abstract:修饰的方法为抽象方法,无方法体。
final:修饰的方法为最终方法,不能由子类改变。
synchronized(同步):修饰的方法执行之前给方法设置同步机制,实现线程同步。
native:修饰的方法为本地方法,即方法实现与本机系统有关。

使用类方法(静态方法)注意:

  1. 在类方法中不能直接引用对象变量。
  2. 在类方法中不能使用super、this关键字(super、this介绍见后)。
  3. 类方法不能直接调用类中的对象方法。类方法是对整个类而言的,而不是针对类的对象,方法体中不能有与对象有关的内容。一些通用的、公用型的方法与类的对象无关,常常被作为类方法实现。调用类方法既可以带类名的限定名称来调用,也可以通过实例来调用。

与类方法相比,对象方法(非静态成员方法)几乎没有什么限制:

  1. 对象方法可以引用对象变量(这是显然的),也可以引用类变量
  2. 对象方法中可以使用supper、this关键字。
  3. 对象方法中可以调用类方法。

递归方法

如果一个方法的方法体中又调用自身,则这种方法被称为直接递归方法。如果一个方法通过调用其它方法而间接地调用到自身,则被称为间接递归方法。

方法的重载与覆盖

方法的重载与覆盖是实现多态的途径。多态是指一个程序中相同的名字表示不同的含义的情况 。

单个类(重载):一个类可以有多个具有相同名字的方法,在使用时由传递给它们的不同个数类型的参数来决定使用哪个方法。方法重载时,返回值的类型可以相同也可以不同,即仅返回值类型不同不能实现重载。除了同一个类,方法的重载也可以在父类和子类之间。

多个类(覆盖):子类对父类方法的覆盖,即子类和父类可以有相同名字的方法,运行的时间决定每个对象到底执行哪个特定的版本。

4.5 构造方法说明

对象的成员变量初始化有三种方式

  1. 自动初始化
  2. 通过一个成员方法显示地初始化为其他值
  3. 定义构造方法时初始化

构造方法是一种特殊的、与类同名的方法,专门用于创建对象,完成初始化工作。构造方法没有返回类型,也不能写void。通常会按照需要定义多个构造方法,即构造方法的重载。

[构造方法修饰符] 方法名([形式参数列表])[throws异常列表]
 {方法体}

构造方法的特殊性

  • 构造方法一般不能由编程人员显式地直接调用,而是用new来调用。
  • 在创建一个类的新对象的同时,系统会自动调用该类的构造方法为新对象初始化。
  • 在Java中,每个类都至少有一个构造方法,如果没有显示地定义构造方法,Java会自动提供一个缺省的构造方法。只要类中显式定义了一个或多个构造方法,而且所有显式定义的构造方法都带参数,那么将失去缺省构造方法。

当构造一个类的实例时,编译器主要完成以下3件事情:

  • 为对象分配内存空间(堆);
  • 按缺省值初始化对象中的实例变量的值;
  • 调用对象的构造方法(可以在构造方法中初始化其他的值)。

构造方法的调用顺序

首先调用父类的构造方法。这个步骤会反复递归,使继承阶层的根源最先被构建。

然后根据各个成员的声明顺序,执行成员变量的初始化赋值

最后执行该构造方法中的各语句。

建议在写多个继承关系的类时,尽量在子类的构造方法中明确使用super调用父类的构造方法

4.6 对象的创建

由new操作符和相应的构造方法完成:

对象名 = new 类构造方法名([实参表]);

new以类为模板,开辟空间并执行相应的构造方法,完成对象的实例化和初始化,并返回该对象的一个引用(即该对象所在的内存首地址)

当类的成员变量规定为private属性时,如果在其它类中要访问该变量,只能通过取数和送数的方法来访问。

4.7 this的使用

Java语言规范中,每个类均有这样3个变量值:null、this、super

this的含义:在方法中,关键词this用来访问本类对象。在普通非static方法中,this表示调用这个方法的对象,而static方法中,不能使用this;在构造方法中,this表示新创建的对象。

在方法及构造方法中,可以使用this来访问对象的属性和方法,在构造方法中,可以用this调用另一构造方法。

通过this不仅可以引用该类中定义的变量和方法,还可以引用该类的父类中定义的成员变量和方法。在所有的非static方法中,都隐含了一个参数this。而static方法中,不能使用this。因为this 代表调用该函数的对象,而静态成员函数可以被所在类直接调用,并不需要创建一个对象。

4.8 继承

在声明类A时,指明类A继承了类B(类B是一个已定义了的类),则类A通常就拥有了类B的成员变量和方法。此时,类B称为类A的父类(superclass),父类也称为超类或基类;类A称为类B的子类(subclass),子类也称为派生类。

Object类是Java中所有类的直接父类或间接父类。

类继承语法格式

class SubClassName extends SupperClassName

extends:继承关键词
SubClassName:新的子类名
SupperClassName:继承的父类名,必须有且只能一个

类拥有其父类的所有属性和方法。但父类中说明为private的属性和方法,子类不可直接访问。子类可以对父类的方法覆盖或重载。

属性的继承、隐藏和添加

继承:子类可以继承父类的所有属性(只要该属性没有private修饰)

隐藏:子类重新定义一个与父类那里继承来的成员变量完全相同的变量,就称作属性的隐藏。

添加:在定义子类时,加上的新的属性变量,就可以使子类比父类多一些属性

方法的继承、覆盖、重载和添加

继承:父类的非私有方法也可以被子类自动继承。在继承机制中,不允许在子类中降低成员(包括变量和方法)的访问权限(访问权限大小关系是private<缺省<protected<public)

覆盖:在子类中定义的方法和父类中的方法的首部是一样的,包括方法名、参数列表、返回类型和异常抛出。但方法体的实现改变了。被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

重载:实际是相当于在子类中新加了一个方法。在使用重载时只能通过不同的参数表样式。不能通过访问权限、返回类型、抛出的异常进行重载。被重载的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重载。

添加:子类可以新加一些方法,以针对子类实现相应的功能

关于静态成员变量的继承

某类的静态成员为该类及该类的所有子类所共有

如果子类中新定义的静态成员变量与父类中的某个静态成员变量同名,则这两个静态成员变量相互独立。

4.9 super的使用

super:代表父类对象,在继承中有重要的作用

使用情况:

  1. 子类隐藏了超类中的变量或方法,而在程序中又要使用超类中被隐藏的变量或方法时
//格式:
super.变量 ; 
super.方法([参数表])
  1. 在子类的构造方法中引用超类的构造方法时
//格式:
super([参数表])

构造方法是不能继承的,但是这并不意味着子类不能调用父类的构造方法。

注意事项:

  • 通过super不仅可以访问直接父类中定义的属性和方法,还可以访问间接父类中定义的属性和方法。如果子类中没有明确使用super,系统会自动在构造方法中加上super(),称为缺省构造方法,来调用直接父类的不带参数的构造方法,如果类中没有定义不带参数的构造方法,则会产生编译错误。

  • 由于它指的是父类对象,所以super不能在static环境中使用,包括类变量、类方法和static语句块。

  • 在构造方法中使用super时,super语句必须放在第一句。

  • 在子类的构造方法中,super可以不明确使用,也可以明确使用。

  • 使用super不能访问本类定义的属性和方法。

4.10 父类对象与子类对象的转换

  • 子类对象转为父类对象时,可以是显示的或隐式的,子类对象直接向父类对象赋值

  • 父类对象不能被任意的转换成某一子类的对象,只有父类对象指向的实际是一个子类对象,那么这个父类对象可以转换成子类对象,但此时必须用强制类型转换。

  • 如果一个方法的形式参数定义的是父类对象,那么调用这个方法时,可以使用子类对象作为实际参数。

4.11 抽象类与抽象方法

定义:abstract关键字修饰的类和方法。

  • 抽象类不能创建任何对象,抽象类必须产生其子类,由子类创建对象。

  • 抽象类中可以包含抽象方法,也可以不包含抽象方法,但如果类中的某一方法是抽象的,整个类就必须被说明成抽象的。

  • 抽象方法在子类中必须被实现,否则子类仍是抽象的。

4.12 final类和final方法

如果一个类被final修饰符所修饰和限定,说明这个类不能被继承,即不可能有子类——最终类。

final修饰符通常是出于安全的目的而使用的,因为不能继承final类,人们就不能重载或覆盖它的任何方法。如果允许一个类被继承,其允许被重载或覆盖的方法可能会被改写。保证某个特定的方法在类层次关系上的某层以后只有一个定义,这对于那些安全性非常关键的类是非常必要的。

final修饰符所修饰的方法,是不能被子类所覆盖的方法。

固定了这个方法所对应的具体操作,可以防止子类对父类关键方法的错误的重定义,保证了程序的安全性和正确性。所有已被private修饰符限定为私有的方法,以及所有包含在final类中的方法,都被默认为是final的。因为这些方法不可能被子类所继承,所以不可能被重载,自然都是最终的方法。

4.13 接口

类只能单继承,而接口可以多继承。类中的方法可以是具体的,也可以抽象的。 接口中的方法都是抽象的。接口中的方法要用类来实现,一个类可以实现多个接口。

[修饰符] interface 接口名[extends] [接口列表]
{
   接口体
}
  • 修饰符:或者不使用修饰符(同包访问),或者只能使用public(任意访问)

  • extends: 定义父接口,支持多重继承。

  • 接口体:定义常量和抽象方法

  • 接口中的成员变量:都是隐含public、static、final的——静态最终变量(常量)

  • 接口中说明的方法都是抽象方法,所有方法隐含public和abstract的

  • 接口中的方法不能使用的修饰符:static、native、synchronized、final

接口自己不能提供方法的实现,接口中的方法必须由类实现。Java语言用关键字implements声明类中将实现的接口。在implements子句中可以包含多个接口类型,各个接口类型之间用逗号隔开。声明接口的形式:

[类修饰符] class类名 [extends子句] [implements子句]

接口可以作为一种引用类型来使用。任何实现该接口的类的实例都可以存储在该接口类型的变量中,通过这些变量可以访问类所实现的接口中的方法。

Person类实现了Runner接口,可以如下赋值,和子类对象赋给父类对象是类似的:

Runner r=new Person();

如果基础类只是定义一些接口,根本不需要具体的实现,那么首先优先选择使用接口,接口的抽象程度比抽象类更高;如果基础类必须实现方法或者定义成员变量的时候,才考虑采用抽象类。

4.14 多态

子类对象可以直接转换成父类对象,即一个对象除了可以当作自己的类型,还可以作为它的基础类型对待,这种把子类型当作它的基本类型处理的过程,就叫作向上转型(即“Upcasting”)

绑定:将一个方法调用同一个方法体连接起来。早期绑定:程序运行以前执行绑定。动态绑定(或后期绑定):在运行期间执行绑定。在Java中所有非final和非static的方法都会自动地进行动态绑定。

4.15 简单工厂模式

补充知识,随便看看。

简单工厂模式又叫静态工厂方法模式,属于类的创建型模式。

简单工厂模式包含的角色及其职责:

  1. 工厂角色[Creator]:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。(根据业务或外界信息动态选择生产哪些产品)

  2. 抽象产品角色[Product]:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口(产品哪些特点,能做什么)

  3. 具体产品角色[Concrete Product]:简单工厂模式所创建的具体实例对象。(产品怎么做)

4.16 包

包是用于组织类的一种方式,可以对类进行分组,一个包中可以包含任意数量的类和接口,本身是一种命名机制,具体的表现就是一个文件夹。包能够形成层次命名空间,缩小了名称冲突的范围,易于管理名称,控制代码访问权限。

创建包就是在当前文件夹下创建一个子文件夹,以便存放这个包中包含的所有类的.class文件

说明格式: package 包名;
例如:
package SubClass;
package MyClass.SubClass;
//说明语句必须放在整个.java文件的第一行 

同包的类相互引用时:在使用的属性或方法名前加上类名作为前缀即可 。不同包中的类相互引用时:在类名的前面再加上包名。

import可以加载整个包中的文件或包中的某一个文件。

import package1[.package2…].(classname|*);
//java编译器会为所有程序自动引入包java.lang

4.17 Java变量及传递

Java按值传递所有参数,制作所有参数的副本,而不管它们的类型。

成员变量是对象的一部分,而对象是存在于堆中的,而局部变量是存在于栈中的。

成员变量如果没有赋初值,则会自动以该类型的默认值(0,false,null等)赋值;而局部变量则不会自动赋值,必须显示地赋值后才能使用。有一种例外情况,被final和static修饰的成员变量必须定义时显示地赋值,而final修饰非static修饰的成员变量也不必须赋值才能使用,不过可以在构造方法中初始化。

数组的元素可认为是数组的成员,所以当数组用new创建并分配空间后,每一个元素会自动地赋值为默认值。

4.18 内部类与匿名类

内部类

含义

内部类(Inner Class),简单地说,就是定义在其他类中的类。

内部类不能与外部类同名(否则,编译器无法区分内部类与外部类),如果内部类还有内部类,内部类的内部类不能与它的任何一层外部类同名。

内部类的修饰符

  1. public、protected、private和缺省:访问权限修饰符,用来限定内部类的访问权限,一般的外部类是不能用protected和private修饰的。访问权限修饰符的限定和成员变量的限定一样。
  2. final:表明内部类不能继承。
  3. abstract:抽象内部类,不能被实例化。
  4. static:表明一个静态内部类

静态内部类和普通的非静态内部类有较大的不同,使用时要遵循如下原则:

  1. 实例化static内部类时,在new前面不需要用对象变量;
  2. static内部类中不能访问其外部类的非static属性及方法,即只能访问static成员;
  3. static方法中不能访问非static的属性及方法,也不能不带前缀地new一个非static的内部类。

内部类中访问外部类的成员

内部类中是可以直接访问外部类的其他属性与方法的,即使它们是private的。

如果内部类中有与外部类同名的属性与方法,可以使用下面的格式来表达外部类的引用,从而区分外部类和内部类的同名的属性与方法:

outerClass.this

内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。

匿名类

含义:类或方法中定义的一种没有类名的特殊内部类。

  • 这种类不取名字,而直接用其父类的名字或者它所实现的接口的名字;

  • 类的定义与创建该类的一个对象同时进行,即类的定义前面有一个new

  • 没有类的首部,对象的创建和类体共同构成一个匿名类表达式,后面以“;”结束

  • 类中不能定义构造方法,因为它没有名字。

  • 在构造对象时不能带参数,因为默认构造方法不能带参数(但如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。

5 Java异常处理

Java把程序运行过程中可能遇到的问题分为两类,一类是致命性的,即程序遇到了非常严重的不正常状态,不能简单地恢复执行,这就是错误(对应Error类),如程序运行过程中内存耗尽。另一类是非致命性的,通过某种处理后程序还能继续运行,这就是异常(对应Exception类)。

5.1 Java异常类的层次

5.2 Exception类

Exception有两种子类:运行时异常和非运行时异常(一般异常)

  1. 运行时异常:RuntimeException类及其所有子类。 运行时异常是程序员编写程序不正确所导致的异常,理论上,程序员经过检查和测试可以查出这类错误。

  2. 非运行时异常(一般异常):指可以由编译器在编译时检测到的、可能会发生在方法执行过程中的异常,如找不到指定的文件等,这不是程序本身的错误,如果这些异常情况没有发生,程序本身仍然是完好的。 

编译器强制要求Java程序必须捕获或声明抛出所有非运行时异常,但对运行时异常不作要求。

不管是运行时异常还是非运行时异常都可以用try-catch-finally来捕获和处理;建议对于运行时异常用户不要去捕获,捕获就是承认这个错误。

5.3 try-catch-finally使用方法

  1. try包含可能出现异常的语句块
  2. 一个或多个catch块紧随try{}块,每个catch块通常处理指定类型的异常。有多个catch块时,要细心安排catch块的顺序。将子类的catch块放在前面,父类的catch块放在后面。
  3. finally引导块紧随catch块后,主要用于清理现场(可有可无)。可为异常处理事件提供一个清理机制,例如清理打开文件、Socket、JDBC连接之类的资源。
try
{ ......
}catch( ExceptionName1 e )
{ ......}
catch( ExceptionName2 e )
{ ......}
finally
{ ......}
//finally总是执行,catch块不一定执行

不要捕获了一个异常而又不对它做任何的处理。

try
{
    …… //正常执行的代码 
}
catch(Exception e) {  }

5.4 声明抛出throws

如果不想捕获和处理异常,可以通过throws语句声明要抛出的异常。

带throws异常说明的方法说明形式如下:

 …  方法名(…) [throws 异常类列表]  
       { 方法体 }
  • 不捕获异常,而是将异常交由上一层处理,在其他地方捕获异常。向编译器表明:此方法可能会抛出异常,但方法本身不会捕获它。

  • 从Error类中派生出的异常和从RuntimeException类中派生的异常不用在方法声明中指定。

5.5 自定义抛出throw

用户可以定义自己的异常类,并用throw语句来抛出。

在捕获一个异常前,必须有一段Java代码来生成和抛出一个异常对象。Java用throw语句抛出异常。throw语句的格式如下:

throw ThrowableObject;

异常对象的生成和抛出可以有以下三种情况:

  1. Java运行时系统
  2. JDK中某个类
  3. 在程序中创建异常对象抛出

抛出异常有这样三步:

  1. 确定异常类
  2. 创建异常类的实例
  3. 抛出异常

6 Java基本类库

6.1 Java语言包(java.lang)

java.lang包中常用的类

Object Java中整个类层次结构的根节点
Class 为运行时搜集的信息
Math 提供数学常数及各种函数
System 提供对操作系统的访问,包括默认的I/O流环境变量、自动垃圾收集、系统时间和系统属性
Runtime 提供了操作系统的访问
Thread 和java.lang.Runnable接口协同作用提供Java中的多线程的支持
Throwable 它是Java中所有异常(Exception)的基类

字符串

  1. String类 :不可改变的静态字符串

String类可创建一个对象,用于代表一个字符串(不变的字符串),并定义了类似查找,比较和连接字符的操作。
所有字符串常量都是String对象,存储在String Pool(字符串池)中,字符串池是常量池的一部分。Java为节省内存空间、提高运行效率,编译时将StringPool中所有相同的字符串合并,只占用一个空间。

在比较字符串内容时,不能用==,而应该用equals方法。String类覆盖了Object类的equals方法

字符串有一些例如求子串、求长度的方法

  1. StringBuffer类:动态可变的字符串

三种创建方法(构造方法)

public StringBuffer();
public StringBuffer(int length);
public StringBuffer(String str);

更新方法

添加:append
插入: insert
修改: setCharAt
删除: delete

Math函数

...

6.2 Java实用包(java.util)

java.util包提供了许多实用的类和数据结构类,如:日期类、日历类、随机数类;向量类、哈希表类、列表类和堆栈类等。

向量类(java.util.vector< E >)

向量类的构造方法

Vector(int capacity, int capacityIncrement);
Vector(int capacity); 
Vector( );

创建向量

Vector<元素类型> 向量名称 = new Vector<元素类型>(容量);
//这里的元素类型不能是基本数据类型
例如:
Vector<String> vector1=new Vector<String>(5); 

在向量中添加元素

boolean add(E e);
例:
vector1.add(”姚明”);  
vector1.add(”韦德”);   
vector1.add(“纳什”);   

获取向量中的某个元素

vector1.elementAt(0);
//获取下标为0的元素,实际上是一个String对象

vector1. elementAt(0).length();
//调用该String对象的成员函数

向量的容量:向量中可存放的元素个数

System.out.println(vector1.capacity());

向量中的元素个数:向量中实际存放的元素个数

System.out.println(vector1.size());
//往向量添加的元素个数超过向量的容量时,向量会自动扩充自己的容量。
//一般扩充为原来的2倍(默认容量增量为0时)。

在向量中插入、修改和删除元素

void insertElementAt(E obj, int index);  //在下标index位置插入元素
void setElementAt(E obj, int index);//修改向量中的元素
vector1. removeElementAt(1);//删除下标为1的元素
vector1.clear(); //向量中的所有元素都被删除

在向量中查找元素

boolean contains(Object o); //判断向量中是否有与某对象等值的对象 
int indexOf(Object o); //查找特定元素在向量中的下标位置,如果有多个相同值的对象,则返回第一个找到的对象的位置如果没找到则返回-1

6.3 Java输入/输出包(java.io)

数据流的概念

数据源:提供数据的地方,如磁盘文件、键盘或网络插口等

数据宿:接收数据的地方,如磁盘文件、显示器、网络插口或者打印机等

流是有两个端口的一条管道,当输入数据时一端与数据源相连,当输出数据时一端与数据宿相连,另一端是程序。流的来源或目的地并不一定是文件,也可以是内存中的一块空间,例如一个字节数组。

数据流的形式

java.io包支持两种类型的数据流:字节流和字符流。

  1. 字节流: 以字节作为基本单位进行读写。InputStream和OutputStream是所有面向字节的输入输出流的超类。

  2. 字符流: 对以字符作为基本单位的数据源或者数据目的进行读写。Reader和Writer是所有面向字符的输入输出流的超类。

注意

  1. java.io.BufferedReader和java.io.BufferedWriter类各拥有8192字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并置入缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。
  2. 从标准输入流System.in中直接读取使用者输入时,使用者每输入一个字符,System.in就读取一个字符。为了能一次读取一行使用者的输入,使用了BufferedReader来对使用者输入的字符进行缓冲。readLine()方法会在读取到使用者的换行字符时,再一次将整行字符串传入。
  3. System.in是一个位流,为了转换为字符流,可使用InputStreamReader为其进行字符转换,然后再使用BufferedReader为其增加缓冲功能 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

在Java程序开始执行时,系统会创建三个流对象:

  1. System.in: InputStream的一个子类的对象,使程序能够读取来自键盘的输入
  2. System.out: PrintStream的一个子类的对象,使程序能够向屏幕输出数据
  3. System.err: PrintStream的一个子类的对象,使程序能够向屏幕输出错误信息

在没有转换之前,标准输入输出流都是字节流,可以通过InputStreamReader和OutputStreamWriter进行转换

char c=(char) System.in.read() ;
System.out.println(c);
/*如果输入‘你’,则输出?
因为相当于读取了字符‘你’的高位字节,并未读取完整的汉字。因此需要将字节流转换为字符流。*/
InputStreamReader isr=new InputStreamReader(System.in);
         try
         {
            c=(char)isr.read();
            System.out.println(c);
         }
         catch(IOException e){}

java.io.File类

在Java中目录和文件都用File类表示,File类建立了Java语言和磁盘文件的联系。File类不能于文件内容的访问。

File类的构造函数

File(String pathname)                //pathname和child指定文件名;
File(File parent, String child) 
File(String parent, String child)  //parent指定目录名,目录名既可以是字符串,也可以是File对象;
File(URI uri)                     //uri是统一资源标识符(见java.net包)

要表示\字符(反斜杠)要用转义字符\\,java中/等同于\\

使用File类创建一个文件

File f1=new File("H:\\a.txt");
boolean success=f1.createNewFile();

使用File类创建一个目录

File f1=new File("H:\\a");
boolean success=f1.mkdir();

文件类操作方法

public boolean createNewFile()
//创建新文件。文件创建成功,返回true;若已有相同文件名存在,返回false。
    
public boolean renameTo(File dest)
//文件重命名。
    
public boolean delete()
//删除文件或空目录。 

目录类操作方法

public boolean mkdir()
 //创建指定目录,建立成功则返回true。
    
public String[] list() 
//返回目录中的所有文件名字符串。
    
public File[] listFiles() 
//返回目录中的所有文件对象。

字节流

InputStream

InputStream是所有面向字节的输入流的超类,抽象了应用程序读取数据的方式。其声明为:

public abstract class InputStream extends Object

主要的方法:

abstract  int read() //从流中读取一个字节填充到int的低8位,如果值是-1就是文件结尾
int read(byte[] b)
int read(byte[] b, int off, int len)//  读取数据到b数组off开始存放len长度的数据

OutputStream

OutputStream是所有面向字节的输出流的超类。抽象了应用程序读取数据的方式。其声明为:

public abstract class OutputStream extends Object

主要的方法:

abstract  void write(int b)            //写一个byte到流,b的低八位
void write(byte[] b)
void  write(byte[] b, int off, int len) //字节数据buf从off位置开始写len长度的字节到流   

FileInputStream

FileInputStream:封装了从文件中读取字节的功能。该类是用来读取字节文件的,如图像文件,如果读取字符或者文本文件,则最好使用FileReader类。

FileInputStream fs=new FileInputStream("a.txt")
File file=new File ("a.txt")
FileInputStream fs=new FileInputStream(file)

FileOutputStream

FileInputStream和FileOutputStream的数据源都是文件,用于进行文件输入输出的处理。

五个构造方法:

FileOutputStream(File file) 
FileOutputStream(File file, boolean append) 
FileOutputStream(FileDescriptor fdObj)     
FileOutputStream(String name) 
FileOutputStream(String name, boolean append)

写入字节的方法:

void write(byte[] b) 
void write(byte[] b, int off, int len) 
void write(int b)  

字符流

FileReader和FileWriter这两个类用于字符文件的输入输出处理,是专门读取和输出字符(文本)文件的类。

FileReader

构造方法

File f=new File(“d:\\t1.txt”); 
FileReader f1=new FileReader(f);
FileReader f2=new FileReader(“d:\\t1.txt”);

重要方法:

read( ) 
read(char b[ ]) 
read(char b[ ],int off,int len) 
close()

FileWriter

构造方法

File f=new File(“d:\\t1.txt”); 
FileWriter f1=new FileWriter(f);   
FileWriter f2=new FileWriter(“d:\\t1.txt”);

重要方法:

write(char c)
write(char b[ ]) 
write(char b[], int off, int len)
close()                //写完后一定要关闭输出流,数据才真正地写到了文件中!

以行为单位从一个文件读取数据(FileReader通过BufferedReader封装,就可以调用readline()一行行读出文件内容)

BufferedReader in = new BufferedReader(new FileReader("F:\\nepalon\\TestIO.java");
String s;
while((s = in.readLine()) != null)   System.out.println(s);
in.close(); 

向一个文件写数据(FileWriter通过PrintWriter封装,就可以调用print()和println()轻松向文件写入内容)

try{
PrintWriter out1 =new PrintWriter(new BufferedWriter(new FileWriter("F:\\nepalon\\ TestIO.out"));
int lineCount = 1;
out1.println(" We are good friends!!!" );
out1.close();
}catch(EOFException ex)
{System.out.println("End of stream" ) ; 

要建立(或打开)一个文件并对它进行输入、输出,需要如下几步:

   1. 用File类建立(或打开)一个文件;
   2. 用FileWriter(FileReader)类建立输出(输入)流;
   3. 调用write(read)进行输出(输入) ;
   4. 关闭输出(输入)流。

7 Java图形用户界面

7.1 GUI支持包

  1. 早期版本的GUI组件

所在的包: java.awt

AWT提供的都是重量级控件,与平台相关性较强

  1. Swing GUI 组件

所在的包: javax.swing

Swing控件属于轻量级控件,Swing组件的平台相关性较小

组件名前一般带个J

注意

  • AWT控件与Swing控件重合时,AWT控件的显示优先级高,不管实际是什么样的遮挡关系,AWT控件总是绘制在Swing控件的上面。尽量不要混用。

  • Swing 与AWT之间的关系不是“替代”,而是“合作”

7.2 容器组件

容器组件是用来放置其它组件的容器,故称为容器组件(Container)。用户可以把各种组件放到容器组件中,甚至把一个容器组件放到另一个容器组件中,因为容器组件也是组件,都是Component类的子类。

  • 各种组件必须放在容器中

  • 容器本身也是一种组件

  • 分类

    • 顶层容器,如JFrame,JApplet,JDialog和JWindow
    • 中间容器,如JPanel,JScrollPane,JSplitPane,JToolBar

建立一个窗口系统,首先需要创建一个图形用户界面,这就需要使用顶级容器类。Window类提供了用于窗口操作的基本功能,但通常使用的是Window类的子类Frame(框架)类和Dialog(对话框)类。

Frame/JFrame

Frame(JFrame) 类用于创建带有菜单条的全功能窗口对象,为窗口、面板等组件提供框架,它可以包含窗口标题、最大化、最小化和关闭窗口等按钮,通常是GUI应用程序窗口的顶层容器组件。

Frame类的对象开始是不可见的,要调用show()方法 (或setVisible(true)方法) 才能显示出来,也可以调用hide()方法将其隐藏。框架对象被创建后就可使用add()方法将其它组件加入到框架中。

Frame和Dialog是Window的子类,它们都是窗口类,默认的布局管理器都是 BorderLayout

Frame类的构造方法有:
Frame()        //创建一个不带标题的框架
Frame(String)  //创建一个带标题的框架

Frame类的常用方法:

show()                    //显示框架
setVisible(boolean b)    //使框架可见/不可见(true/false)
hide()                  //隐藏框架
setTitle()             //设置框架的标题
setSize(int w, int h) //调整框架的尺寸(宽/高为w/h)
setBounds(int x, int y, int w,int h)  //调整框架的位置及尺寸(左上角为(x,y), 宽、高为w、h)
add(Component ob)   //将其它组件ob加入到框架的中心位置 
add(String p, Component ob)  //将组件ob加入到框架的p位置 (框架默认的布局方式是BorderLayout,它将容器划分为东西南北中)

Dialog/JDialog

对话框类Dialog (JDialog)的对象是有标题条而无菜单条和最小化按钮图标的容器组件,它必须依附在某个窗口上(如Frame),一旦它所依附的窗口关闭了, 对话框也自动关闭。

对话框通常用于在应用程序中弹出一个窗口, 用于提示输入数据、保存文件等。

有两种模式的对话框:

  • 响应模式: 对话框出现期间,所依附窗口不接收任何操作

  • 非响应模式: 对话框出现时, 与所依附窗口都可同时接收操作

可以用下列构造方法创建普通对话框:

Dialog(Frame)  
//创建依附于Frame的无模式对话框

Dialog(Frame,boolean)      
//创建对话框,并由布尔值的真假决定此对话框有无模式

Dialog(Frame,String)              
//创建无模式对话框,并给定对话框的标题

Dialog(Frame, String, boolean)  
//创建对话框,指出是否有模式,并给定对话框的标题 

panel/Jpanel

面板panel(Jpanel)是能在屏幕上实际显示的组件,提供了容纳其他组件的功能,但本身必须放在Window,Frame,Dialog等容器中才能使用

所有面板的默认的布局管理器是FlowLayout,即按照从左至右、从上到下的方式布局

7.3 基本组件

  • 基本控制组件,如JButton, JComboBox, JList, JMenu, JSl ider, JTextField

  • 不可编辑的信息显示组件,如JLabel, JProgressBar, JToolTip

  • 可编辑的信息显示组件,如JColorChooser, JFileChooser, JTable, JTextArea

Button/JButton

构造方法有二个:

JButton( )               //生成一个没有标记的按钮
JButton(String label)      //生成一个带标记label的按钮

常用方法:

setLabel(String label)                        //设置按钮标记 
getLabel( )                                  // 获取按钮标记
setActionCommand(String s)                  //设置用户按下按钮时返回的信息
addActionListener(ActionListener l)        //将l指定为按钮的监听者
removeActionListener(ActionListener l)    //将l从按钮监听者中去掉

Label/JLabel

标签(Label)是一种只能用来显示单行文本的组件。

标签在容器中的对齐方式有三种:左对齐、居中和右对齐, 用Label.LEFT、Label.CENTER、Label.RIGHT三个静态常量表示,在程序中可以设置其对齐方式。

标签类有三个构造方法:

Label( )                           //生成一个空标签
Label(String text)                   //生成一个带有指定文本的标签
Label(Strlng text, int alignment)   //生成一个带有指定文本和在容器中的对齐方式的标签

标签类的常用方法

getAlignment( )                          //获取对齐方式
getText( )                              //获取文本
setAlignment(int aligmnent)            //设置对齐方式
//JLabel中是调用setHorizontalAlignment(SwingConstants.CENTER);
setText(String text)                 //设置文本 

TextField/JTextField

文本框(TextField)和多行文本区域(TextArea)是用来显示和输入文本的控件,它们都是TextComponent的子类。

构造方法有

TextField( )                               //创建一个空的文本框
TextField(Strint text)                    //创建一个带有初始文本的文本框
TextField(int Columns)                   //创建一个指定列数的文本框    
TextField(String text, int colulmns)    //创建一个指定列数和带有初始文本的文本框 

常用方法

addActionListener(ActionListener l)      //将l指定为文本框的ActionEvent事件监听者 
removeActionListener(ActionListener l)  //将l从文本框的监听者中去掉
setEchoChar(String sc)                 //设置用户输入的回应字符
getEchoChar( )                        //获取回应字符 
setText(String s)                    //设置文本框中的字符串 
getText( )                          //获取文本框中的字符串 

TextArea/JTextArea

文本区域(TextArea)也称为多行文本框,除了多了处理大量文本的功能以外,其余同单行文本框(TexiFieid)是一样的。单行文本框在尺寸上有限制而且不能卷动,而多行文本框可以指定其高度和宽度,而且具有滚动条,因此它可以处理较大数量的文本。

构造方法

TextArea()                   //创建一个0行长, 0列宽的空多行文本框
TextArea(int,int)           //创建一个具有给定行数,给定列数的空多行文本框
TextArea(String)           //创建一个具有给定字符串的多行文本框
TextArea(String,int,int)  //创建一个具有给定字符串、给定行数、列数的多行文本框

复选框和单选钮、下拉式列表、列表、进度条(滚动条)、菜单...

7.4 组件在容器中的布局

布局管理器的优点是能根据不同的屏幕自动进行排版,缺点是控件在屏幕上的确切位置难以确定。

顺序布局FlowLayout

顺序布局是指将组件从左到右依次排列,一行排满就转到下一行继续排列,直到所有的组件都排列完毕。是面板Panel和它的子类Applet的默认布局方式。

//把Frame容器的布局管理器设置为
Frame f=new Frame();
f.setLayout(new FlowLayout());

构造方法

public FlowLayout()
//创建一个新的FlowLayout,其默认值是居中对齐,默认组件彼此有5单位的水平与垂直间距。

public FlowLayout(int align)
//创建一个新的FlowLayout,此FlowLayout可以设置对齐(align)方式,对齐必须是LEFT、CENTER或RIGHT之一。默认组件彼此有5单位的水平与垂直间距。

public FlowLayout(int align,int hgap,int vgap)
//创建一个新的FlowLayout,可以自己设置对齐方式、水平间隔和垂直间隔。

边界布局BorderLayout

是容器JFrame和JApplet的默认布局方式
将容器分成五个区域,NORTH(顶部)、SOUTH(底部)、WEST(左侧)、EAST(右侧)、CENTER(中间)。每个区域最多只能1个组件。

构造方法:

BorderLayout() 
//构造一个组件之间没有间距的新边界布局。

BorderLayout(int hgap, int vgap) 
//用指定的组件之间的水平间距构造一个边界布局。

将组件添加到BorderLayout布局的容器中的方法

add(new Button(" South "), BorderLayout.SOUTH); 
add(new Button("South"),"South");
add("South",new Button("South"));

网格布局GridLayout

网格布局是把容器区域分为若干个网格,每个网格可以放置一个组件,这种布局方式非常适合数量庞大的组件。

网格布局比顺序布局多了行和列的设置,也就是说要先设置网格布局共有几行几列。然后加进去的组件会先填完第一行格子,然后再从第二行开始填,依此类推,就像是一个个的格子一般。而且网格布局会将填进去的组件大小设为一样。

构造函数

public GridLayout()
public GridLayout(int rows,int cols)
public GridLayout(int rows,int cols,int hgap,int vgap

Rows: 行数
Cols: 列数
hgap:组件之间的水平间隔
vgap:组件之间的垂直间隔

卡片布局CardLayout 、网格包布局GridBagLayout ...

7.5 事件处理

事件监听器

事件监听器就是一个接收事件、解释事件并处理用户交互的方法。

Java中采用委托模型的方式处理事件。即事件产生以后,不是由事件源或者其容器对象处理事件,而是将事件委托给第三方对象——事件监听器来处理。监听器包含事件处理程序。

每类事件都有对应的事件监听器,监听器是接口,根据动作来定义方法。

大多数事件监听器都有对应的适配器,例如:WindowListener的适配器是WindowAdapter,它是java.awt.event包中定义的实现了WindowListener接口的抽象类

以响应窗口关闭事件为例。当点击窗口的关闭按钮时,会产生窗口关闭事件,这时必须在该事件的处理函数中调用退出函数,窗口才能正常关闭,否则关闭不了。

Frame f=new Frame("我的第一个窗口");//事件源为窗口本身

//将窗口的窗口事件交给WindowListener监听器处理
f.addWindowListener(new MyWindowAdapter());

//在事件监听器的相应事件函数中实现自己的功能
class MyWindowAdapter extends WindowAdapter
{
    public void windowClosing(WindowEvent e)
    {
        System.exit(0); //正常退出            
    }
}

产生windowClosing事件后,系统会自动调用事件监听器的windowClosing函数。

7.6 字体和颜色

颜色和字体    setBackground, setForeground, setFont
位置和大小    setBounds, setLocation, setSize

7.7 图形绘制

静态绘制

负责图形绘制的类:

  • Graphics类:包括绘制直线、矩形和椭圆等方法,但是绘制图形的操作能力非常有限。例如不能改变线的粗细,不能旋转这些图形。

  • Graphics2D类:Java SE 1.2引入了一个功能更强大的图形操作类 ,它是Graphics类的子类。

要绘制图形,必须具备两个要素:画布和画笔

Swing中任何JComponent类的子类都可以充当画布的角色,任何java.awt.Component类的子类都可以作为画布

所有swing控件都有一个paint方法,负责在需要的时候对控件进行绘制public void paint (Graphics g). paint方法中的参数g就是画笔,paint方法是自动调用的,当第一次显示组件或改变组件的大小需要重新画组件的界面时,该方法都会由系统自动调用

实际开发中,通常都是采用继承JComponent或JPanel类并重写paint的方式来获得画布和画笔的,然后加到顶层容器中;也可以直接重写窗口Frame类的paint方法。

绘制图形采用的是笛卡尔坐标系统,该坐标都是以像素为单位。画布上左上角为该坐标的原点(0,0)位置,x轴向右延伸,y轴向下延伸。

AWT提供了一个用于绘图的类——Graphics,它可以用来绘制字符串和图形(直线、矩形、椭圆 、弧线等)。其成员方法有:

drawString
drawLine
drawRect
draw3DRect
drawOval 
drawArc 
drawPolygon 
fillRect
fill3DRect 
fillOval
fillArc
fillPolygon

关于动画的开发

使用画布和画笔不但可以实现绘制静态的图形,配合线程或时钟类Timer还可以开发出灵活的动画效果。

让程序根据一定的规则不断地对画布进行重新绘制,即将绘制的规则编写到paint方法中,定时让paint方法重新调用实现重画,但是,要实现重绘必须调用repaint方法间接调用paint,不能直接调用paint方法重绘制。

开发动画需要定时执行指定的任务,可以自己开发一个线程,也可以用javax.swing.Timer类。

重写paint方法后,别忘了在方法体第一句加上super.paint(g);

8 Java多线程

程序:一段静态的代码,应用程序执行的蓝本。
进程:程序的一次动态执行过程。
线程:程序内部的控制流,比进程更小的执行单位。一个进程在执行过程中,为了同时完成多个操作,可以产生多个线程,形成多条执行线索。

8.1 多线程实现机制

方法一 继承Thread类方法

声明一个 Thread 类的子类,并覆盖 run() 方法

class MyThread extends Thread 
{
    public void run() 
    {
    /* 覆盖该方法*/ 
    }  
}
/**通过Thread实例的start(),一个Thread的实例只能产生一个线程
*定义一个Thread类的子类
*覆盖 run( )方法  
*创建对象(用构造方法)
*调用该对象的start()方法,将该线程启动
*注意不能直接调用run()方法, start()方法引起run的调用
*事实上,Thread类本身就实现了Runnable接口
*/

方法二 实现Runnable接口(提倡使用)

声明一个实现 Runnable 接口的类,并实现 run() 方法

class MyThread implements Runnable
 { 
     public void run()
     {
     /* 实现该方法*/ 
     } 
 }
/**Runnable实例需要被Thread对象来包装才能运行,同一实例可产生多个线程
*定义一个实现Runnable接口的类
*实现run()方法(必须实现)
*将该类的对象作为Thread类构造方法的参数,创建一个线程实例
*调用该对象的start()方法启动线程
*/ 

Thread类的重要方法

yield()方法: 与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会,如果没有同等优先级的线程是可运行状态,yield()方法将什么也不做。

join()方法: t.join()方法使当前线程等待,直到 t 结束;有三种调用格式:
join(): 当前线程等待线程t结束后再继续执行
join(long millis): 当前线程将等待线程t结束或最多等待mills毫秒后再继续执行
join(long millis,long nanos) throws InterruptedException

interrupt()方法:中断线程。如果一个线程t在调用sleep()join()wait()等方法被阻塞时,则t.interrupt()方法将中断t的阻塞状态,并将接收到InterruptException对象

isDaemon()setDaemon()方法:线程分为守护线程与用户线程
当一个程序中只有一个守护线程在运行,程序会立即退出;如果一个程序还有正在运行的用户线程,该程序不会中止判断一个线程是用户线程还是守护线程,用isDaemon()方法,将一个线程设置为守护线程用setDaemon()方法。

线程的生命周期

New(新生,创建) 线程至今尚未启动
Runnable(可运行) 正在 JVM中执行的线程
Blocked(被阻塞) 受阻塞并等待某个监视器锁的线程
Waiting(等待) 无限期地等待另一个线程来执行某一特定操作的线程
Timed waiting(计时等待) 等待另一个线程来执行取决于指定等待时间的操作的线程
Terminated(被终止) 已退出的线程

要确定一个线程的当前状态,可调用getState方法

8.2 多线程的同步互斥

当两个或多个线程要同时访问共享数据时,会产生潜在的资源冲突, 要十分小心地加以控制,以便一次只能有一个线程访问共享数据。

解决的方法:给共享的资源(临界资源)加锁(临界区)。当一个线程在使用这个资源时该资源就被锁住, 其他线程就不能使用, 除非解锁。

具体的方法:把方法声明为synchronized,便可有效地防止冲突。Java中每个对象都包含了一把锁(也叫监视器),它自动成为对象的一部分. 调用任何synchronized方法时,对象就会被锁定,其他任何synchronized方法不可调用该对象。

synchronized的缺点:提供与每个对象相关的隐式监视器锁的访问,强制所有锁获取和释放均要出现在一个块结构中。当获取多个锁时,必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁

8.3 线程死锁

假设线程甲、乙共享表1和表2,线程甲修改表1涉及修改表2,线程乙修改表2涉及修改表1

//线程甲                                  
synchronized 修改表1(){                 
       ……A1                                   
       修改表2(); //调用修改表2()方法        
        ……                                      
}
//线程乙
synchronized 修改表2(){
       ……A2
      修改表1(); //调用修改表1()方法
       ……
}

如果甲线程进入修改表1,但是尚未调用修改表2(),这时乙线程进入修改表2,接着乙线程如果试图调用修改表1(),也会被阻隔,甲线程如果还试图进入调用修改表2就会被阻隔,这样甲、乙线程都处于僵持挂起状态。这叫线程死锁。

原文地址:https://www.cnblogs.com/gylic/p/15776414.html