一、理论
1. Java程序的加载与执行
Java 程序的运行包括两个重要的阶段:编译,运行
编译阶段
主要任务是检查Java源程序是否符合Java语法,符合则能够生成正常的字节码文件。字节码文件不是纯粹的二进制文件,这种文件无法在操作系统中直接执行。
过程:
- 程序员需要在硬盘的某个位置新建一个
.java
扩展名的文件,该文件被称为Java文件,源文件中写的是Java源代码。 - Java 程序员需要在 JDK 中自带的 Java.exe 命令进行 Java 程序的编译。javac 是一个 java 编译器工具。
javac 在 DOS 窗口中使用 - 一个java 源文件可以编译生成多个
.class
文件,故在 class 文件删除之后, java 源文件删除不会影响程序的执行。 - 编译结束后,可以将
class
文件拷贝到其他操作系统中执行
运行阶段
- JDK 安装之后,除了自带一个
java.exe
之外,还有另一个工具/命令,叫做java.exe
,主要负责运行阶段,在 DOS窗口中使用。
java 类名
- 过程:
- 打开DOS命令窗口
- 输入:
java A
- java.exe 命令会启动Java虚拟机(JVM),JVM会启动类装载器ClassLoader
- ClassLoader会去硬盘上搜索
A.class
文件,找到该文件则将该字节码文件装载到 JVM 当中。 - JVM 将
A.class
字节码文件解释成二进制数据 - 最后操作系统执行二进制和底层硬件平台进行交互
2. JDK/JVM/JRE
JDK:java 开发工具包
JRE:realtime environment
JVM:java 虚拟机
3. 杂货
Windows 操作系统搜索硬盘上某个命令的方式:
- 首先会在当前目录下搜索
- 会从环境变量 path 指定的路径当中搜索某个命令
- 如果都搜索不到,则报错
----解决方案:增加环境变量:path=xxx;D:\Project_Java\jdk8u312-b07\bin
DOS运行java程序:(以“HelloWorld.java文件为例”)
-
需要使用 java.exe 命令
-
首先测试 java 命令是否可用
-
通过 javac.exe 生成HelloWorld.class 文件
-
使用方式:
java 类名
硬盘上有HelloWorld.class ,那么类名就是: HelloWorld
java HelloWorld
一定要注意:java命令后面跟的不是文件路径,是一个类的名字 -
首先需要将DOS窗口中的目录切换到 HelloWorld.class 文件所在的目录
-
然后直接执行:
java HelloWorld
-
HelloWorld.java文件源代码如下:
public class HelloWorld //表示定义一个公开的类,起名 HelloWorld { //类体中不允许直接编写java语句,声明变量除外 /* public 表示公开的 static 表示静态的 void 表示空 main 表示方法名是main (String[] args) 是一个main 方法的形式参数列表 已下的方法是一个程序的主方法,是程序的执行入口 */ public static void main(String[] args){ //表示定义一个公开的静态的主方法 //方法体 //方法体 //方法体 //java语句【java语句以";"终止】 System.out.println("Hello World"); //向控制台输出一段消息:Hello World //java中的所有字符串都用双引号 } }
关于java语言中的注释:
-
出现在 java 的源程序中,对 java 源代码解释说明
-
注释不会被编译到
.class
字节码中 -
一个好的开发习惯应该是多编写注释,这样可以增强程序可读性!!!
-
书写方式:
//单行注释 /* 多行注释 多行注释 多行注释 */ /** * javadoc注释 * javadoc注释 * javadoc注释 */
public class 和 class 的区别
- 一个java源文件中可以定义多个 calss
- 一个 java 源文件中不一定有 public class
- 一个class会定义生成一个 xxx.class 字节码文件
- 一个java源文件当中定义公开的类的话,public class 只能有一个,并且该类的名称必须和java的源文件名称一致
classpath
- 默认情况下,ClassLoader 从当前路径下加载xxx.class字节码文件
- 当然,也可以让ClassLoader去某个指定的路径加载字节码文件,这时需要配置环境变量classpath
- classpath环境变量属于java语言中的环境变量,不属于windows操作系统【PATH环境变量属于操作系统】
- classpath是给ClassLoader类加载器指路的。
- 设置这样的环境变量:
classpath=D:\course\JavaProjects\02-JavaSE\1
– 打开dos命令窗口在任意位置打开,都可以执行:java HelloWorld - 当classpath 环境变量没有配置的话,类加载器默认从当前路径下找字节码文件,
当classpath 环境变量配置为某个指定路径之后,类加载器只会去指定的路径中加载字节码文件。
二、编程语言基础
1.标识符
–什么是标识符?
- 在java源程序中凡是程序员有权利自己命令的单词都是标识符。
- 标识符在EditPlus编辑器中以黑色字体高亮显示。
- 标识符可以标识类名、方法名、变量名、接口名、常量名。
–标识符的命名规则?
- 只能由数字、字母、下划线、美元符号组成。
- 不能以数字开头
- 严格区分大小写
- 关键字不能做标识符
- 理论上无长度限制
–标识符的命名规范?
- 类名、接口名:首字母大写,后面每个单词首字母大写
- 变量名、方法名:首字母小写,后面每个单词首字母大写
- 常量名:全部大写
2.变量
–什么是变量?
- 变量本质来说是内存中的一块空间,这块空间有数据类型、名字、字面值(数据)
- 变量是内存中存储数据的最基本的单元
–数据类型的作用?
- 不同的数据有不同的类型,不同的数据类型底层会分配不同大小的空间。
- 数据类型是指导程序在运行阶段应该分配多大的内存空间
–变量的声明语法格式: 数据类型 变量名
–访问变量的两种形式:
get
获取:读取变量中保存的具体数据set
设置:修改变量中保存的具体数据
java中的变量必须先声明,再赋值,才能访问!
–变量的分类:
根据变量声明的位置来分类:
局部变量:在方法体中声明的变量叫局部变量
成员变量:在方法体外【类体之内】声明的变量叫做成员变量
public class VarTest04
{
//主方法:入口
public static void main(String[] args){
int i = 10; //i是局部变量
//java遵循就近原则
System.out.println(i);
}
int i = 100; //i是成员变量
//System.out.println(i); 类体中不能直接编写java语句【除声明变量之外】
public static void doSome(){
//局部变量
int i = 90;
}
}
–变量类型的转换:
- 系统自动转换:
在系统自动转换时可以把所占内存空间字节较少的类型自动转换成所占内存字节较多的类型。
(byte/short/char)--int--long
、float--double
、int--double
- 强制类型转换:
(目标类型)原类型表达式
3.数据类型
–java中数据类型包括两种:基本数据类型、引用数据类型:
- 基本数据类型:【四大类八小种】
第一类:整数型byte/short/int/long
第二类:浮点型float/double
第三类:布尔型boolean
第四类:字符型char
- 引用数据类型:
第一类:类
第二类:数组
第三类:接口
字符串不属于基本数据类型,字符串属于引用数据类型
–八种基本数据类型各自占用空间大小:
byte
1short
2int
4long
8float
4double
8boolean
1char
2
注意:基本数据类型在内存中存放的是数据值本身,引用数据类型在内存中存放的是指向该数据的地址。
–8种基本数据类型的取值范围:
byte
[-128~127]short
[-32768~32767]int
[-2147483648~2147483647]boolean
[true, false]char
[0~65535]
注意:short和char 所表示的种类总数是一样的,只不过char可以表示更大的正整数,因为char没有负数。
–关于8种基本数据类型的默认值(成员变量没有手动赋值系统会默认赋值,局部变量不会):
byte/short/int/long
0(L)float/double
0.0(F/D)boolean
falsechar
‘\u0000’
01 字符型
public class DataType
{
public static void main(String[] args){
//定义一个char类型的变量,起名c,同时赋值字符'a'
char c = 'a';
System.out.println(c);
//一个中文占用2个字节,char类型正好是2个字节
//所以java中的char类型变量可以存储一个中文字符
char x = '中';
System.out.println(x);
//char y = 'ab';
//char k = "z";
}
}
JDK中自带native2ascii.exe命令,可以将文字转换成unicode编码形式,在命令行中输入 native2ascii
,回车,然后输入文字即可得到unicode码。
java常用转义字符:
\ddd
:八进制数表示的Unicode字符\uxxxx
:十六进制数表示的 Unicode 字符\'
:单引号\"
:双引号\r
:回车\n
:换行\f
:换页\b
:后退一格
02 整数型
- java语言中的“整数型字面值”被默认当作int型来处理。要让这个“整数型字面值”被当做long类型处理的话,需要在“整数型字面值”后面添加I/L,建议使用大写L。
- java语言当中的整数型字面值有三种表达方式:
第一种:十进制(缺省默认)
第二种:八进制(以0开头)
第三种:十六进制(以0x开头) - 以十进制方式输出
public class DataType
{
public static void main(String[] args){
int a = 10;
int b = 010;
int c = 0x10;
System.out.println(a); //10
System.out.println(b); //8
System.out.println(c); //16
int i = 123;
System.out.println(i);
//456整数型字面值被当做int类型,占用4个字节
//x变量在声明的时候是long类型,占用8个字节
//int类型的字面值456赋值给long类型的变量x,存在类型转换
//小容量可以自动转换成大容量,成为自动转换机制。
long x = 456;
System.out.println(x);
//long z = 2147483648; 编译错误
//2147483648一开始被当做int类型处理,但这个字面值超过int类型的范围。
long z = 2147483648L;
System.out.println(z);
long c = 100L;
int d = (int)c;
//大容量转换成小容量,需要进行强制类型转换,加“强制类型转换符”
//强制转换后编译通过了,但是运行阶段可能会损失精度。
/*
强转原理:
原始数据:00000000 00000000 00000000 00000000 00000000 00000000 0000000 01100100
强转之后的数据:00000000 00000000 0000000 01100100
将左边的二进制直接砍掉
*/
//大容量的int类型转换成小容量的byte类型,理论上应报错
byte m = 50;
/*在java语言当中,当一个整数型字面值没有超出byte、short、char类型的取值范围话,可以直接赋值给byte、short、char
变量,目的是为了方便程序员编程*/
byte n = (byte)128;//-128
}
}
03 浮点型
float
: 单精度【4个字节】;double
:双精度【8个字节,精度较高】
SUN在基础SE类库中为程序员准备了精度更高的类型,只不过这种类型是一种引用数据类型,不属于基本数据类型。java.math.BigDecimal
在java程序中SUN提供了一套庞大的类库,java程序员是基于这套基础的类库来进行开发的。所以要知道java的SE类库的字节码在哪:
- SE类库字节码:
D:\Project_Java\jdk1.8.0_311\jre\lib\rt.jar
- SE类库源码:
D:\Project_Java\jdk1.8.0_311\src.zip
在java语言中,所有的浮点型字面值,默认被当做double类型来处理。
要想该字面值当做float类型来处理,需要在字面值后面加上F/f
注意:double 和 float 在计算机中内部二进制存储时存储的都是近似值。
public class DataTypeTest01
{
public static void main(String[] args){
//3.0是double类型字面值
//d是double类型的变量,不存在类型转换
double d = 3.0;
System.out.println(d);
//5.1是double型的字面值,f是float类型的变量,大容量转小容量需要加强类型转换符
//故以下程序编译错误。
//float f = 5.1;
//解决方案:
float f = (float)5.1; //强制类型转换
float f = 5.1f; //没有类型转换
}
}
04 布尔型
在java中,boolean类型只有两个值:true 和 false,不像C中,0和1可以表示假和真。
05 数据类型转换
关于基本数据类型之间的互相转换:
-
八种基本数据类型当中除布尔型外都可以互相转换
-
小容量向大容量转换,称为自动类型转换,容量从小到大排序:
byte < short < int < long < float < double
char <注:任何浮点数据类型不管占用多少个字节,都比整数型容量大。
char 和 short 可以表示的种类数量相同,但是char 可以取更大的正整数。 -
大容量转换为小容量,称为强制类型转换,需要加强类型转换符,程序才能通过编译,但是在运行阶段可能会损失精度,所以谨慎使用
-
当整数字面值没有超出byte、short、char的取值范围,可以直接赋值给byte、short、char变量
-
byte 、short、char混合运算时,先各自转换成int类型再做运算。
-
多种数据类型混合运算,先转换成容量最大的那种类型再做运算。
4.运算符
01算术运算符
注意:求余运算符(%)10 % 4 = 2
、25.3 % 12 = 1.3
一元运算符与操作数之间不允许有空格
02 关系运算符
> >= < <=
的优先级要高于 == !=
的优先级
注意:不能在浮点数之间进行精确的 == 比较
03 字符串连接运算符
当“+”两边的数据都是数字时,一定进行加法运算。
当“+”两边只要有一个数据是字符串,一定会进行字符串连接运算。并且,运算结果仍然是一个字符串。
在一个表达式中可以出现多个“+”,在没有添加小括号的前提下,遵循自左向右一次运算
public class OperaterTest
{
public static void main(String[] args){
int a = 10;
int b = 20;
System.out.println(10 + 20); //这里的加号是求和
System.out.println(10 + 20 + "30"); //自左向右的顺序依次运算,第一个是求和,第二个是字符串连接
System.out.println("10 + 20 = " + a + b); //都是字符串连接
System.out.println(a +" + "+ b +" = "+ (a + b));
//引用类型String
//String类型是SUN在Java中提供的字符串类型
int i = 10;
String username = "abc";
System.out.println("登陆成功,欢迎"+ username +"回来!");
}
}
04 赋值运算符
注意: 扩展运算符不改变原字面值的数据类型!因此,扩展运算符与基本运算符作用并不完全一样!
byte b = 10;
//b = b + 5;
//编译错误:编译器只检查语法,不运行程序,发现b+5为int类型
b = (byte) (b + 5);
b += 5;//可以
byte z = 0;
z += 128;//等同于 z = (byte)(z + 128),其实不等于 z = z + 128
z += 1000;//z = (byte)(z + 1000),得到-112
5.Scanner键盘输入
使用System.in输入数据的技术称为标准输入、控制台输入。使用System.in实现方便键盘输入的常用方法是将系统类Scanner的对象和System.in 对象结合在一起使用。
Scanner s = new Scanner(System.in);
–步骤:
- 程序引入
java.util
包,生成Scanner类对象。 - 在生成Scanner 对象之后,可以调用它自有的方法进行数据输入。
–输入六种数值数据类型的方法:
- nextByte():
byte b = scanner.nextByte();
- nextDouble():
double d = scanner.nextDouble();
- nextFloat():
float f = scanner.nextFloat();
- nextInt():
int i = scanner.nextInt();
- nextLong():
long l = scanner.nextLong();
- nextShort():
short s = scanner.nextShort();
import java.util.*;
public class ifTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("How old are you? ");
int age = in.nextInt();
System.out.println("Hello,"+"Next year, you'll be "+(age+1));
}
}
–键盘输入字符串:
-
读入一个单词,使用next 方法,以空格为结束符
Scanner scanner = new Scanner(System.in); String name; System.out.print("输入一个字符串:"); name = scanner.next(); //next方法以空格和回车作为分隔符
-
读入一行字符串,使用nextLine 方法,可以输出空格
Scanner scanner = new Scanner(System.in); String name; System.out.print("输入一行字符串:"); name = scanner.nextLine(); //nextLine方法以回车作为分隔
三、程序流程控制
1.控制选择结构语句
01 if,if…else
–编写方式:
-
第一种:
if(布尔表达式){ java语句; }
-
第二种:
if(布尔表达式){ java语句; }else{ java语句; }
-
第三种
if(布尔表达式){ java语句; }else if(布尔表达式){ java语句; }else if(布尔表达式){ java语句; }
-
第四种
if(布尔表达式){ java语句; }else if(布尔表达式){ java语句; }else if(布尔表达式){ java语句; }else(布尔表达式){ java语句; }
02 switch
–编写方式:
switch(表达式){
case 表达式常量1:语句1;
case 表达式常量2:语句2;
case 表达式常量n:语句n;
[default:语句n+1;]
}
import java.io.*;
class WeekDayTest
{
public static void main(String args[])throws IOException
{
int w;
System.out.print("请输入有个有效星期数(0~6):");
w = System.in.read()-48;
switch(w)
{
case 0: System.out.println(w +"表示是星期日");
break;
case 1: System.out.println(w +"表示是星期一");
break;
case 2: System.out.println(w +"表示是星期二");
break;
case 3: System.out.println(w +"表示是星期三");
break;
case 4: System.out.println(w +"表示是星期四");
break;
case 5: System.out.println(w +"表示是星期五");
break;
case 6: System.out.println(w +"表示是星期六");
break;
default: System.out.println(w +"是无效数!");
}
}
}
–注意:
- 表达式只能是byte、char、short、int类型,不能使用float、booeal、long类型和字符串。
- 当case语句中包括多行运行语句时,无需用花括号。
2.控制循环结构语句
01 for
02 while
03 do…while
3.改变控制语句顺序
01 continue
–使用方法:
-
不带标号:此时
continue
语句用来结束本次循环。 -
带标号:此时
continue
语句跳过标号所指语句中多重循环所有余下的语句,回到标号所指语句块的条件测试部分,进行条件判断。
要给一个程序块加标号,只需在相应程序块前面加一个合法的 Java 标识符,并在后面跟上一个冒号。import javax.swing.JOptionPane; public class ContinueLabelTest { public static void main(String args[]) { String output = ""; rownext: for (int row=1; row <= 5; row++) { output += "\n"; for (int column = 1; column <= 10; column++) { if (column > 2*row-1) continue rownext; output += "*"; } } JOptionPane.showMessageDialog(null, output, "testing continue with a label", JOptionPane.INFORMATION_MESSAGE); System.exit(0); } }
02 break
–使用方法:
- 不带标号:无条件终止
break
所在的那一层的循环语句,转去运行其后的第一条语句。 - 带标号:典型用法是,从其所处的多层循环的内部直接跳出来,只要在欲跳出的循环开始处加上标号即可。
4.递归
class factor
{
public long factorial(long n)
{
if(n==1) return 1;
else return n*factorial(n-1);
}
}
public class LoopDGc {
public static void main(String args[])
{
long n = 20l;
factor a = new factor();
// long result = a.factorial(n);
System.out.println(" "+n+" 的阶乘为:"+a.factorial(n));
}
}
四、数组
数组是由相同类型的一系列元素组成的集合。
1.一维数组
–声明:
类型[] 数组名;
或 类型 数组名[]
注意:Java 在数组声明时并不为数组分配内存空间,因此,在方括号中不能给出数组的长度!
–内存申请:
引用变量实际上保存的是数组或对象在堆内存中的首地址,在程序中使用栈的引用变量访问堆中的数组或对象。
int[] a;
a = new int[10];
若想释放数组内存,使任何引用变量不指向堆内存中的数组对象,则将常量null赋值给数组即可,即 a = null;
–初始化:
-
静态初始化:
格式一:直接为每个元素赋值,直接分配内存空间。数据类型 数组名[] = {值1, 值2,值3, ..., 值n}
int a[] = {1,2,3};
格式二:使用 Scanner 系统类,从键盘输入数组元素值。
**注意:**先声明数组,再采用静态初始化方法来初始化数组在Java程序中是不允许的。
-
动态初始化:
需要使用 new 操作符来分配内存空间,既可以在声明时初始化,又可以在声明后初始化。
格式一:使用 new 运算符指定数组大小后进行初始化。int c[] = new int[3]; int c[]; c = new int[3];
格式二:使用 new 运算符指定数组元素的值。
int a[]; a = new int[]{1,2,3}
–for each 语句与数组:
for each 循环能够在不使用下标的情况下遍历数组。
public class TestArray{
public static void main(String[] args){
double[] myList = {1.9, 2.9, 3.4, 3.5};
//打印所有元素
for(double element: myList){
System.out.println(element);
}
}
}
2.二维数组
–声明:类型 数组名[][];
或 类型[][] 数组名;
–创建:
-
直接为每维分配长度、大小:
int a[][] = new int[2][3];
-
从高维的第一个下标开始,分别为每维分配空间:
int b[][] = new int[2][]; b[0] = new int[3]; b[1] = new int[5];
此时创建的不是一个标准的矩阵。
–初始化:
-
动态初始化:用 new 运算符分配内存空间,再为元素赋值。
int a[][] = new int[2][3];//定义一个2行3列的二维数组 a[0][0] = 33;
-
动态初始化:直接对每个元素进行赋值,在声明和定义数组的同时也为数组分配内存空间。
int a[][] = {{2,3}, {1,3}, {2,3}}
五、Java类和对象
1.面向对象编程
封装性:
把对象的属性和服务结合成一个系统单位,并尽可能隐蔽对象的内部细节。
类是具有相同属性和行为的一组对象和一般描述,是对封装的软件实现。
继承性:
是一种由已有类创建新类的机制,是面向对象程序设计的基石。
注意:Java只支持单继承,C++支持多继承。
多态性:
通过方法重载、方法重构及抽象类等技术实现。多态就是指多种方式。
消息:
对象之间是通过消息相互联系作用的。
2.类的描述
01 类的定义
class 类名{
[成员变量];
[成员方法];
}
注意:类的成员变量必须放在类体中,但又不能包含在某个方法中。
[public] [abstract/final] class 类名{类体}
–类的修饰符:
- public公共类:类可以被任何包中的类使用,即本类是开放的。一般情况下把包含主方法的类定义为public公共类,并以publib公共类的名称作为源文件的文件名。
- abstract抽象类:抽象类不能实例化一个对象,只能被继承。
- final最终类:表示该类不能被继承。
注意:Java不关心修饰词出现的次序,但final 和 abstract 不能同时修饰一个类。
02 成员变量的访问控制
成员变量的声明必须放在类体中,通常是在成员方法之前。在方法中声明的变量不是成员变量,而是方法的局部变量。
成员变量的格式:[访问控制符] 变量类型 变量名[=初值];
–访问控制符:
- public 修饰符:成员变量可以被任何类访问。
- private 修饰符:成员变量只能被这个类自有的方法访问。
- protected 修饰符:成员变量可以被本类、类的子类(可以在不同包下)和同一个包的其他类访问。
- friendly 默认访问权限:成员可以被类的本身和同一个包内的类访问。
03 成员方法
[方法修饰符] 方法返回值列表 方法名([形式参数列表]){
[局部变量列表;]
[语句块;]
}
**在声明局部变量时应注意:**在变量类型前不能加修饰符;在声明时若未对变量赋值,则局部变量没有默认值。
class Rectangle{
double width,height;
Rectangle(double w, double h){ //类的构造方法
width = w; height = h;
}
double area(){ //求矩形面积的方法
return width * height;
}
}
public class RectangleTest {
public static void main(String args[])
{
double s;
Rectangle myRect = new Rectangle(20, 30); //创建对象myRect
s = myRect.area(); //调用对象方法 area 求矩形面积
System.out.println("Rectangle的面积是:"+s);//输出面积
}
}
04 成员变量、局部变量和final
–局部变量和成员变量的区别:
- 成员变量属于类,而局部变量是在方法中定义的变量或是方法的参数。
成员变量可被public、private 和 static等修饰符修饰,而局部变量不能被访问控制符和static所修饰。 - 成员对象存储在堆内存,局部变量存在栈内存。
- 成员变量随对象的创建而存在,局部变量随着方法的调用而产生,随方法调用的结束而自动消失。
- 成员变量若没有被赋初值,则会以对应类型的默认值赋值;而局部变量则不会自动赋值,必须显式地赋值后方可使用。
–final 变量:
由final 修饰的成员变量实际是一个常量,它的值在程序运行过程中不能被改变。
final double PI=3.14159
常量名一般使用大写字母。
3.对象的创建与使用
在 Java 中,对象的创建、使用和释放称为对象的生命周期。
01 对象的创建
包括:对象声明、对象实例化、对象初始化。
**对象声明:**由类名和对象名组成。
**对象实例化:**在声明对象时,并没有给该对象分配存储空间。对象实例化可以完成对象的空间分配。由 new
运算符完成。
**对象初始化:**由构造方法完成。
new
运算符返回一个引用。对象引用实际是一个引用变量指向对象所在内存的首地址。
02 对象的使用
–对象的一般使用:
- 通过对象引用对象的成员变量:
对象名.变量;
- 通过对象调用对象的成员方法:
对象名.方法名([参数列表]);
–对象作为数组元素:
在创建对象数组时,需要首先声明数组并用new运算符给数组分配内存空间,然后再对数组的每个对象元素初始化。
class Node{ //节点类的定义
private int data; //对象本身就是一个地址的概念
private Node next;
void setData(int x)
{
data = x;
}
int getData()
{
return data;
}
Node getNext()
{
return next;
}
void setNext(Node x)
{
next = x;
}
}
public class ObjArray { //主类
public static void main(String args[]) {
Node x[] = new Node[3]; //创建3个节点对象,对象作为数组元素
int i;
for(i = 0; i < x.length; i++) //初始化对象数组元素
x[i] = new Node();
for(i = 0; i < x.length; i++) //给节点data赋值,并组成链表
{
x[i].setData(i);
if(i < x.length-1) x[i].setNext(x[i+1]);
}
Node start = new Node(); //利用start依次输出链表中点的值
start = x[0];
System.out.println(start.getData());
while(start.getNext()!=null)
{
start = start.getNext();
System.out.println(start.getData());
}
}
}
**–对象作为类的成员变量:**类成员包含其他类的对象。
03 匿名对象
一个对象被创建后,在调用该对象的成员方法时,也可以不定义对象的名称,而直接调用该对象的方法,这样的对象称为匿名对象。
Person p1 = new Person();
p = new Person();
p.speak();
可改写为:
new Person().speak();
使用匿名对象的情况:
- 若对一个对象只需要一次方法调用
- 将匿名对象作为实参传递给一个方法调用
4.类的构造方法
构造方法时一种特殊的方法,其名称与所在类的类名必须完全相同,无返回值,不加void。且:
- 构造方法只能由 new 运算符调用。
- 构造方法不能被继承,但子类可以调用父类的构造方法。
- 不允许构造函数指定返回值类型或返回值,void 也不能使用
- 构造方法可以重载。
若类中没有定义构造方法,则编译器会自动创建一个不带参数的默认构造方法。【无参构造方法】
类的实例变量初始默认值:
布尔型:false
字符型:’\u0000’
整型:0
浮点型:0.0
引用:null
this 引用*
泛指对 this 所在类的对象自身的引用。
1. 用this指代成员变量:
class Demo{
double x,y;
Demo(double x, double y){this.x = x; this.y = y;}
double ave(){return (x+y)/2;}
}
class TestThis1{
public static void main(String args[]){
Demo s = new Demo(3,4);
System.out.println(s.ave());
}
}
在一个方法中,若有方法参数与某个类的成员变量同名,则当访问该类成员变量时,应显式使用 this
2. 在构造方法中使用 this 调用一般方法:
class Demo{
int x,y;
Demo(int a, int b){
x = a;
y = b;
this.sort(a,b); //这里this可以不写
}
void sort(int a, int b)
{
int t;
if(x < y){t = x; x = y; y = t;}
}
}
class TestThis2{
public static void main(String args[]){
Demo m1 = new Demo(12,20);
System.out.println(m1.x+" "+m1.y);
}
}
3. 在一个构造方法中调用另一个构造方法:
this 关键字必须写在构造方法的第一行!
class Cylinder{
private double radius;
private int height;
private double pi = 3.14;
String color;
public Cylinder() {
this(2.5, 5, "橙色"); //调用另一个构造方法,this调用需放在第一行
System.out.println("无参构造方法被调用了");
}
public Cylinder(double r, int h, String str) {
System.out.println("有参构造函数被调用了");
radius = r;
height = h;
color = str;
}
public void show() {
System.out.println("圆柱底半径为:"+radius);
System.out.println("圆柱体的高为:"+height);
System.out.println("圆柱体颜色为:"+color);
}
double area() {
return pi*radius*radius;
}
double volmn() {
return area()*height;
}
}
public class TestThis3 {
public static void main(String args[]) {
Cylinder c = new Cylinder();
System.out.println("圆柱底面积:"+c.area());
System.out.println("圆柱体体积:"+c.volmn());
c.show();
}
}
运行结果:
有参构造函数被调用了
无参构造方法被调用了
圆柱底面积:19.625
圆柱体体积:98.125
圆柱底半径为:2.5
圆柱体的高为:5
圆柱体颜色为:橙色
5.static 变量及 static 方法
使用static修饰的成员变量称为类的静态变量,不把它视为实例对象的成员变量。
静态变量是类所有的,直接通过类名引用。
01 static 变量
静态变量是一个公共的存储单元,当任何一个类的对象访问它时,取得的都是相同的数值。同样,当任何一个类的对象去修改它时,也都是在对同一个内存单元进行操作。
–静态变量的使用格式:
类名.静态变量名;
或 对象名.静态变量名;
类中若含有静态变量,则静态变量必须独立于成员方法之外。
02 static 方法
静态方法实质上是属于整个类的方法,而不加static 修饰符的方法,是属于某个具体对象的方法,成为实例方法。
–静态方法的使用格式:
类名.静态方法名();
或 对象名.静态方法名();
静态方法中不能使用 this 或 super 。因为它们都代表对象的概念,this 代表本类的对象,super代表上层父类的概念。
static 方法只能访问 static 成员变量或调用 static 成员方法。
6.对象初始化过程
class Bird{
public Bird(String name) {
System.out.println("Bird "+name+" 3");
}
void feed() {
System.out.println("feed()4");
}
}
class Animal{
Bird b1 = new Bird("HL");
static Bird b2 = new Bird("static HL"); //静态实例变量
static { //静态初始化器,静态代码块
b2.feed();
}
public Animal() {
System.out.println("Animal");
}
public Animal(String name) {
System.out.println("Animal "+name+" 2");
}
}
public class Insects extends Animal{
public Insects(String name) {
super(name);
System.out.println("insects "+name+" 1");
}
public static void main(String args[]) {
Insects a1 = new Insects("ant");
Insects a2 = new Insects("ant2");
}
}
运行结果:
Bird static HL 3 静态代码只会执行一次
feed()4
Bird HL 3 创建Animal类中的成员变量b1的结果
Animal ant 2 a1的Insects构造方法执行
insects ant 1
Bird HL 3 对象a2构造方法执行之前,先执行b1的创建,静态代码不再执行
Animal ant2 2
insects ant2 1
首先执行的是源代码中父类 static 静态代码,包括 static 变量声明和被 static 修饰的代码块,且只执行一次。
其次是父类 b1 的创建。
然后是类 Insects 的对象 a1 和 a2 的构造方法。
另外,子类 Insects 不仅拥有父类的属性和成员方法,在没有定义自己的构造方法时,也还会继承父类的无参数的构造方法。
静态初始化器与构造方法之间的比较:
- 构造方法是对每个新创建的对象初始化,而静态初始化器是对类自身进行初始化。
- 构造方法是在用 new 运算符创建新对象,它是由系统自动调用的;而静态初始化器一般不能由程序来调用,它是在所属的类被载入内存时由系统调用执行的。
- 用 new 运算符创建多少个新对象,构造方法就被调用多少次;但静态初始化器被载入时只执行一次。
- 不同于构造方法,静态初始化器不是方法而是代码块。
7.成员方法
01 方法调用与参数传递
–Java 语言的参数传递:
- 基本数据类型的参数传递:传值调用
- 引用类型的参数传递:传地址引用调用
–方法调用的形式:
- 调用对象成员方法的一般形式:
对象名.方法名([实际参数列表]);
- 静态方法属于类方法,调用的一般形式:
类名.方法名([实际参数列表]);
02 方法重载
注意:方法不能以返回值类型来区分重载方法!
public class OverLoadDemo {
void overload() {
System.out.println("第一次重载!");
}
void overload(String str) {
System.out.println("第二次重载!"+str);
}
void overload(String str1, String str2) {
System.out.println("第三次重载!"+str1+str2);
}
public static void main(String args[]) {
OverLoadDemo strdemo = new OverLoadDemo();
strdemo.overload();
strdemo.overload("Java");
strdemo.overload("Love","China");
}
}
03 final 最终方法和 abstract 抽象方法
1. final 最终方法:
最终方法能被子类方法继承和使用,但不能在子类中修改或重新定义。
2. abstract 抽象方法:
抽象方法指没有方法体,即没有实现的方法,含有抽象方法的类成为抽象类。
六、类的继承和接口
1.类的继承
–子类的创建:
[修饰符] 子类名称 extends 父类名称{
类体
}
在类的定义中,若没有使用extends,则默认该类继承 Object 类。
**注意:**Java程序在运行子类的构造方法前,先调用父类的构造方法。父类若提供了有参构造方法,没有提供无参构造方法,在主类中调用子类的无参构造创建对象时,则会产生语法错误。解决方法:在父类中添加一个形式上的无参构造方法即可:public Person(){}
。
2.隐藏与重构
01 成员变量的隐藏
当子类隐藏了父类的同名成员变量后,实际上子类就有了两个同名的成员变量。子类若要引用父类中的同名成员变量:
super.成员变量名;
或 父类名.成员变量名; //仅适用于static变量
02 成员方法的覆盖
–重载与覆盖的区别:
方法重载是指在同一个类中有若干同名而参数不同的方法,使用不同的参数个数和参数类型可以分别调用同名方法的不同版本;
方法覆盖是指在子类中用与父类中相同的方法名、方法类型和参数,重新构造父类的某个成员方法。
父类的非private 方法会被子类自动继承。
–子类实现对父类方法的覆盖,必须满足以下3个条件:
- 完全相同的方法名
- 完全相同的参数列表
- 完全相同类型的返回值
java 解释器会沿着继承链查找,选择正确的方法绑定到当前对象,并产生不同的响应结果。
3.抽象类
由关键字 abstract 说明的类为抽象类,通常抽象类包含抽象方法。抽象方法是指有访问修饰词、返回值类型、方法名和参数列表,而无方法体且无包含方法体的花括号的方法。
子类在继承抽象类时,必须重写其父类的抽样方法,给出具体的定义。
构造方法、静态方法、私有方法不能成为抽样方法。不可以用 new 创造抽象类的实例,抽象类只能用于父类派生子类。
当子类继承抽象类时,若没有实现父类所有的抽象方法,则该子类仍为抽象类。
abstract class Graphics{
abstract void parameter(); //参数处理
abstract void area(); //面积处理
}
class Rectangle extends Graphics{
double h,w;
Rectangle(double u, double v){h = u; w = v;}
void parameter() {
System.out.println("矩形高度为:"+h+", 矩形宽度为:"+w);
}
void area() {
System.out.println("矩形面积为:"+(h*w));
}
}
class Circle extends Graphics{
double r;
String c;
Circle(double u, String v){r = u; c = v;}
void parameter() {
System.out.println("圆的半径:"+r+", 圆的颜色:"+c);
}
void area() {
System.out.println("圆面积:"+(Math.PI*r*r));
}
}
public class Exam {
public static void main(String args[]) {
Rectangle rec = new Rectangle(2.0,3.0);
Circle cir = new Circle(4.0,"Red");
Graphics[]g = {rec, cir};
for(int i = 0; i < g.length; i++)
{
g[i].parameter(); //根据对象类型的不同启动不同的parameter方法
g[i].area(); //根据对象类型的不同启动不同的area方法
}
}
}
抽象类用于规定其子类必须具有的一组方法的方法头。利用抽象类和抽象方法可以使方法头的设计和方法的实现分开,有利于控制程序的复杂性。
4.接口
接口(Interface)是一组抽象方法和常量的集合,可认为是一种只有常量和抽象方法的特殊抽象类。接口在方法协议和方法实现间起到一种称为界面的作用,界面规定了方法实现中的方法名、参数类型、参数个数及方法返回类型一定要与方法协议中所规定的保持一致。因此类与接口之间并不存在着子类与父类的那种继承关系。一个类可以实现多个接口。
01 接口的定义与类定义
–接口的定义:
[public] interface 接口名 [extends 父接口列表]
{
[public static final] 类型 常量名 = 值;
[public abstract] 返回值类型 接口方法名(形参表);
}
接口的方法默认 public abstract 属性;接口的变量成员默认 public static final 属性。
–接口的类定义:
[类访问控制修饰词] class 类名 [extends 父类名] implements 接口列表
{类体}
实现接口的类必须实现接口中的每个方法,包括接口的父接口中定义的方法。若有一个接口方法未实现,则该类必须定义为抽象类,否则编译出错。
import javax.swing.JOptionPane;
import java.text.DecimalFormat;
interface Shape {
public abstract double area();
}
class Circle implements Shape{
protected double radius;
public Circle() {setRadius(0);}
public Circle(double r) {setRadius(r);}
public void setRadius(double r) {radius = (r>0?r:0);}
public double getRadius() {return radius;}
//实现接口 Shape 的 area 方法
public double area() {return Math.PI*radius*radius;}
}
class Triangle implements Shape{
protected double x,y;
public Triangle() {setxy(0,0);}
public Triangle(double a, double b) {setxy(a,b);}
public void setxy(double x, double y) {this.x = x; this.y = y;}
public double getx() {return x;}
public double gety() {return y;}
//实现接口 Shape 的 area 方法
public double area() {return x*y/2;}
}
public class shapeTest {
public static void main(String args[]) {
Circle c = new Circle(7);
Triangle t = new Triangle(3,4);
String output = "";
DecimalFormat p2 = new DecimalFormat("0.00");
//在对话框中输出实例圆和三角形的面积
output += "\n 半径为"+c.getRadius()+"圆的面积:"+p2.format(c.area());
output += "\n 底为"+t.getx()+",高为"+t.gety()+"三角形面积:"+p2.format(t.area());
JOptionPane.showMessageDialog(null, output, "接口实现和使用演示",
JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
java.awt
包中的JOptionPane
类用于创建基本对话框:
showMessageDialog
用于创建消息对话框,仅显示输出字符串或结果showInputDialog
用于创建输入数据的人际对话框,显示提示信息和一个文本输入框showConfirmDialog
用于创建确认对话框,向用户提示简单的“yes or no”问题,并要求用户确认
上述静态方法一般有四个参数:
- 对话框的父组件,若没有则设成
null
,此时对话框将显示在屏幕中间 - 对话框显示的消息
- 对话框的标题
- 对话框的消息类型
JOptionPane.ERROR_MESSAGE
显示错误消息,图标为“-“JOptionPane.INFORMATION_MESSAGE
显示信息,图标为“i”JOptionPane.WARNING_MESSAGE
显示警告消息,图标为“!”JOptionPane.QUESTION_MESSAGE
显示问题消息,图标为“?”JOptionPane.PLAIN_MESSAGE
显示标准消息,无图标
02 Java 8 接口扩展方法
Java 8 允许给接口添加一个非抽象的方法
interface Formula{
double calculate(int a);
default double sqrt(int a){
return Math.sqrt(a);
}
}
Foemula 接口除拥有 calculate 方法之外,同时还定义了 sqrt 方法,实现 Formula 接口的子类只需实现一个 calculate 方法, 默认方法 sqrt 将在子类上直接使用。
5.泛型
泛型(Generic Structure)的好处是可以在编译时检查类型安全,并且所有的强制转换是自动和隐式的,提高了代码的重用率。
声明如下:
class 名称<泛型类型变量>
泛型的类型只能是类类型(包括自定义类),不能是简单类型。
class Gen<T>{
private T ob; //定义泛型成员变量
public Gen(T ob){ //参数使用泛型成员变量
this.ob = ob;
}
public T getOb() { //返回值类型为泛型类型
return ob;
}
public void setOb() {
this.ob = ob;
}
public void showType() {
System.out.println("T的实际类型是:"+ob.getClass().getName());
//使用系统方法
}
}
public class GenDemo {
public static void main(String args[]) {
//定义泛型类Gen的一个Integer版本
Gen<Integer> intOb = new Gen<Integer>(88);
intOb.showType(); //使用泛型类中的方法
int i = intOb.getOb(); //使用泛型类中的方法
System.out.println("value="+i);
System.out.println("---------------------");
//定义泛型类Gen的一个String版本
Gen<String> strOb = new Gen<String>("Hello Gen!");
strOb.showType(); //使用泛型类中的方法
String s = strOb.getOb(); //使用泛型类中的方法
System.out.println("value="+s);
}
}