文章目录
前言
这个博文仅用于个人记录学习过程,学习心得,以便日后复习。本人使用的网课教程来源于
Java零基础教程视频(适合Java 0基础,Java初学入门)
一、HelloWorld源程序
展示了Java源程序的基本结构
注:
System.out.println(); //输出以后换行
System.out.print(); //输出以后不换行
代码实例:HelloWorld源程序
/*
1、Java中任何有效的代码必须写到“类体”当中,最外层必须是一个类的定义。
2、public表示公开的,class表示定义一个类,HelloWorld是一个类名。类名后面必须是一对大括号,这一对大括号被称为“类体”
3、括号必须是要成对的,建议成对编写。
4、Java对缩进不严格,缩进是为了更好的阅读
*/
public class HelloWorld { //声明(定义)一个公开的类,名字叫HelloWorld
//类体
/*
1、整个这一块代码被称为main方法(主方法)(程序的入口),任何程序都要有一个入口。
2、方法必须要放到“类体”中。
3、JVM在执行程序的时候,会主动去找这样一个方法。
*/
public static void main(String[] args){//这是一个入口方法,arges是可以更改的
//方法体
/*
注意:
1、方法体由一行一行的“Java语句”构成。
2、任何一条Java语句都必须以英文“;”结尾,“;”表示一条语句的结束。
3、方法体中的代码遵循自上而下的顺序逐行执行。
*/
System.out.println("HelloWorld!");//向控制台输出“HelloWorld!”
System.out.println(50);//数字
System.out.println(1+1);//公式计算,加减乘除都可
System.out.println("1+1");//字符串
}
}
二、类体
1、一个Java源文件可以定义多个class。
2、在源文件只要有一个class的定义,就会对应生成一个class文件。
3、public类不是必须的,可以没有。若有,其有且只有一个。
4、public修饰的类名必须和源文件名保持一致。
5、每个类中都可以编写一个入口。
代码实例:
public class Class_Knowledge_Point {
}
class A{
public static void main(String[] args) {
}
}
class B{
}
class C{
}
三、标识符(与C语言类似)
Java严格区分大小写
3.1 标识符的命名规则
1.1 标识符由数字、字母(包括中文)、下划线、美元符号组成
1.2 标识符不能以数字进行开头
1.3 关键字不能做标识符
1.4 标识符严格区分大小写
3.2 标识符的命名规范
2.1 见名知意
2.2 驼峰命名法
2.3 类名,接口名的特殊要求:首字母大写,后面每个单词首字母大写
2.4 变量名,方法名的特殊要求:首字母小写,后面每个单词首字母大写
2.5 所有常量名每个字母都大写,单词之间采用“_”相连
四、变量(与C语言类似)
4.1 字面量
字面量:在Java语言中,数据被称为字面量
4.2 变量
变量:用于存储数据且值可以改变
变量三要素:数据类型、变量名、值
变量声明/定义的语法格式(与C语言类似):
数据类型 变量名;
例:int nianLing;(ps:Java语言有规定,变量必须先声明,再赋值才可以访问)
同一个域中变量不能重复声明
代码实例:
public class Variate {
public static void main(String[] args) {
int nianLing = 20; //变量的定义与赋值
int a,b,c =100; //Java一行可以声明多个变量,但是a,b没有赋值,只有c赋值了100
System.out.println(nianLing);
/*
若直接写 System.out.println(a);
System.out.println(b);
则会出现错误:
The local variable a may not have been initialized
The local variable b may not have been initialized
原因是:a,b没有赋值
*/
a = 100;
b = 100;
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
4.3、变量的作用域
4.3.1 变量的分类
根据变量的出现的位置划分:
在方法体中声明的变量叫做:局部变量
注:局部变量只在方法体中有效,方法体执行结束后该变量的内存就释放了。
在方法体外,类体内声明的变量叫做:成员变量
4.3.2 变量的作用域
变量的作用域是变量的有效范围(出了大括号就不认识了)
访问:Java有就近原则
代码实例:作用域
public class ZuoYongYu {
int a=1000;//成员变量
public static void main(String[] args) {
int a=100;//局部变量,作用域是main函数
System.out.println(a);//就近原则,输出100
for(int k=0;k<10;k++) {//k的作用域是for函数
}
/*
若编写 System.out.println(k);则会报错
原因是k的作用域是for函数
*/
int i;
for(i=0;i<10;i++) {
}
System.out.println(i);
}
}
五、Java数据类型
5.1 数据类型
5.1.1 数据类型的作用
数据类型用来声明变量,程序在运行过程中根据不同的数据类型分配不同大小的空间。
5.1.2 数据类型的分类
第一种:基本数据类型
整数型
byte(占用1个字节),int(占用2个字节),short(占用4个字节),long(占用8个字节)
浮点型
float(占用4个字节),double(占用8个字节)
布尔型
boolean(占用1个字节)
字符型
char(占用2个字节) (Java中规定字符型字面量必须使用单引号括起来)
第二种:引用数据类型
5.1.2.1 整数型
①在Java语言中,整数型字面量有4种表示形式,分别是:二进制,八进制,十进制(最常用),十六进制。
②在Java语言中整数型的字面量默认被当作int类型处理,若希望该“整数型字面量”被当作long类型处理,需要在其后面加L/l。
③int不超范围可以直接赋值给byte,short,char,并且会做自动类型转换。
④多种数据类型做混合运算时,最终结果的类型是最大容量对应的类型,但是byte,short,char数据类型做混合运算时,会各自转化成int在做运算。
代码实例:
public class IntTest {
public static void main(String[] args) {
int a = 10; //十进制表示
System.out.println(a); //输出10
int b = 010; //八进制表示,0....
System.out.println(b); //输出8
int c = 0x10; //十六进制表示,0x...
System.out.println(c); //输出16
int d = 0b10; //二进制表示,0b...
System.out.println(d); //输出2
char c1 = 97; //int不超范围可以直接赋值给char,并且会做自动类型转换
System.out.println(c1); //输出a
char c2 = 'A';
byte b1 = 2;
System.out.println(c2 + b1);
/*
若写 short x = c2 + b1;
则会报错 错误原因是c2 + b1 运算以后是int型的,不能直接转化为short型的
*/
}
}
5.1.1.2 浮点型
Java中规定任何一个浮点型数据都默认被当作double型来处理。如果想让这个浮点型字面量被当作float型来处理,那么要在这个字面量后加F/f
5.1.1.3 布尔型
在Java语言中,boolean类型只有两个值:true和false;不像C或者C++,C语言中1和0也可以表示布尔型
5.2 类型转换
类型转换规则
①八种基本数据类型中,除 boolean类型不能转换,剩下七种类型之间都可以进行转换。
②如果整数型字面量没有超出 byte,short,char的取值范围,可以直接将其赋值给byte,short,char类型的变量。
③小容量向大容量转换称为自动类型转换,容量从小到大的排序为: byte < short(char)<int < long< float< double,其中 short和 char 都占用两个字节,但是char可以表示更大的正整数。
④大容量转换成小容量,称为强制类型转换,编写时必须添加“强制类型转换符”,但运行时可能出现精度损失,谨慎使用。
⑤byte,short,char类型混合运算时,先各自转换成int类型再做运算。
⑥多种数据类型混合运算,各自先转换成容量最大的那一种再做运算。
5.3 转义字符
\n :表示换行
\t :表示tab键
\\ :表示普通的反斜杠
\" :表示普通的双引号
\’ :表示普通的单引号
\u :表示后面的是一个字符unicode编码
六、运算符(和C语言很相似)
6.1 算数运算符
+,-,*,/,%,++(自加一),-- (自减一)
主要注意++和-- 的位置
代码实例:a++与++a
public class Operator {
public static void main(String[] args) {
int a = 10;
System.out.println(a++); //输出10
//上面语句可以拆分为
//int temp = a++;
//System.out.println(temp);
System.out.println(a); //输出11
int b = 20;
System.out.println(++b); //输出21
System.out.println(b); //输出21
}
}
6.2 关系运算符
<,<=,>,>=,==,!=
6.3 逻辑运算符
逻辑与:&
逻辑或:|
逻辑非:!
短路与:&&
短路非:||
短路与和短路非具有短路现象:&&左边为false时,||左边为true时右边表达式不执行。(结果确定时,右边表达式不执行)
在开发中,大部分情况下用&&,||,只有当两边表达式均执行时,用&,|
代码实例:短路现象代码
/*
短路现象代码
*/
public class Operator {
public static void main(String[] args) {
int x = 20;
int y = 21;
System.out.println(x > y & x > y++);
System.out.println(y); //输出22
int m = 20;
int n = 21;
System.out.println(m > n && m > n++);
System.out.println(n); 输出21
}
}
6.4 赋值运算符
=,+=,-=,*=,/=,%=,^=,&=,|=,<<=,>>=
x += 10类似于x = x + 10;但是二者不完全相同
Java中的语法机制:使用扩展运算符时,永远都不会改变运算结果类型。
代码实例:类型转换与赋值运算
public class Operator {
public static void main(String[] args) {
byte b1 =100;
byte b2 =100;
/*
若写:b1 = b1 + 1;
则会报错,因为int不能直接转换为byte
*/
b1 += 1;
b2 += 105;//虽然超出范围了,但是不会报错,会损失精度
System.out.println(b1); //输出101
System.out.println(b2); //输出-51
}
}
6.5条件运算符(三目)
语法格式:布尔表达式 ? 表达式1 : 表达式2
运行原理:当布尔表达式值为true时,表达式1的结果作为整个表达式的结果;当布尔表达式值为false时,表达式2 的结果作为整个表达式的结果。
代码实例:三目运算
public class Test {
public static void main(String[] args){
int a , b;
a = 10; // 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
System.out.println( "Value of b is : " + b ); // 如果 a 等于 10 成立,则设置 b 为 20,否则为 30
b = (a == 10) ? 20 : 30;
System.out.println( "Value of b is : " + b );
}
}
6.6 字符串连接运算符(+)
当 + 运算符任意一边为字符串类型时,那么就会进行字符串的拼接操作。
当表达式中有多个 + 号时,遵循自左向右依次执行
public class Operator {
public static void main(String[] args) {
int a = 100;
int b = 200;
int nianLing = 20;
System.out.println("年龄=" + nianLing); //输出 年龄=20
System.out.println(a + b + "50"); //输出30050
}
}
6.7 运算符的优先级
七、控制语句(和C语言基本相同)
7.1 选择(分支)语句
7.1.1 if语句
所有的if语句,一个分支执行,整个语句结束。
语法格式
if (布尔表达式1) {
语句块1;
} else if (布尔表达式2) {
语句块2;
} else if (布尔表达式3) {
语句块3;
}……else(布尔表达式n) {
语句块n;
代码实例:根据年龄判断人所处的时期
/*
根据年龄判断人所处的时期
要求:
1.从键盘上接收一个人的年龄
2.年龄范围为[0,135],超过范围提示非法信息
3.[0,6] 婴幼儿
[7,17] 少年
[18,40] 青年
[41,65] 中年
[66,135] 老年
*/
public class IfTest {
public static void main(String[] args) {
java.util.Scanner s=new java.util.Scanner(System.in);
System.out.print("请输入年龄:");
int age = s.nextInt();
if(age < 0 || age > 135) {
System.out.println("输入年龄非法!");
}
else if(age <= 6) {
System.out.println("婴幼儿");
}
else if( age <= 17) {
System.out.println("少年");
}
else if( age <= 40) {
System.out.println("青年");
}
else if( age <= 65) {
System.out.println("中年");
}
else {
System.out.println("老年");
}
}
}
7.1.2 switch语句
语法格式
switch (值) {
case 值1:
语句块1;
break;
case 值2:
语句块2;
break;
……
case 值n:
语句块n;
break;
default:
语句块n+1;
}
注:① switch语句中,break和default不是必须的
② switch语句仅支持int类型和String类型数据,但是byte,short,char可以进行自动类型转换
③ 如果分支执行了,但是没有break,会出现“case穿透”现象
代码实例:系统接收一个学生的考试成绩,并根据考试成绩输出成绩的等级
/*
Switch实例
项目要求:
1、系统接收一个学生的考试成绩,并根据考试成绩输出成绩的等级
2、优[90,100]
良[80,90)
中[70,80)
及格[60,70)
不及格[0,60)
3、成绩可以取小数
*/
public class SwitchTest {
public static void main(String[] args) {
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请输入成绩:");
float chengJi = s.nextFloat();
int i;
if(chengJi > 100 || chengJi < 0) {
System.out.println("输入成绩错误!");
}else {
i = (int)(chengJi)/10;
String str = "不及格";
switch(i) {
case 9:case 10:
str = "优";
break;
case 8:
str = "良";
break;
case 7:
str = "中";
break;
case 6:
str = "及格";
}
System.out.println("该学生的成绩等级为:" + str);
}
}
}
7.2 循环语句
7.2.1 for循环
语法格式
for (初始化表达式;条件表达式;更新表达式) {
循环体;
}
注:① 初始化表达式最先执行,并且在整个循环中只执行一次
② 条件表达式的结果必须要是一个boolean类型
③ 初始化表达式,更新表达式有时可以缺省
代码实例:for循环实现99乘法表
/*
for循环实现99乘法表
*/
public class ForTest {
public static void main(String[] args) {
int z;
for(int x = 1 ; x < 10 ; x++) {
for(int y = 1 ; y <= x; y++) {
z = x * y;
System.out.print(" " + x + "*" + y + "=" + z);
}
System.out.println();
}
}
}
7.2.2 while循环
语法格式
while(条件表达式){
循环体;
}
注:① 循环体中要注意更新条件变量,防止出现死循环
② while循环和for循环差不多
7.2.3 do…while…循环
语法格式
do{
循环体;
} while(条件表达式) ;
注:① 循环体中要注意更新条件变量,防止出现死循环
② do…while…循环会先循环一次再进行判断
③ while后不要漏掉分号
7.3 转向语句
7.3.1 break语句
注:① break语句用在switch语句中用来终止switch语句的执行(防止case穿透)
② 用在循环语句当中,用于终止循环(离break最近的)
③ break可以终止指定的循环
step1 给循环命名
step2 终止
代码实例:break终止指定的循环
/*
break终止指定的循环
*/
public class BreakTest {
public static void main(String[] args) {
a:for(int i = 0 ; i < 2 ; i++) { //将for循环标识为a
for(int j = 0 ; j < 10 ; j++) { //将for循环标识为b
if(j == 5) {
break a;
}
System.out.println("j=" + j);
}
}
}
}
7.3.2 continue语句
终止本次循环,直接进入下一次循环
7.3.3 return语句
8、方法
方法是可以完成某个特定功能的并且可以被重复利用的代码片段。
8.1 方法与C语言函数的区别
(1)“函数”是一段实现某种“功能”的代码,函数的操作是对输入数据的处理。
函数的英文function恰恰有“功能”的含义,可以达到“见名知意”。通过函数的名称来实施函数调用。它能将一些数据(也就是参数)传递jQuery进行处理,然后返回一些数据(即函数的返回值),也可以没有返回值。所有传递给函数的数据都是显式传递的。而方法的参数传递通常是隐式的,它可以直接操作类内部的数据。
(2)“方法”也是一段完成某项功能的代码,也通过名字来进行调用,但它依赖于某个特定的对象。
例如,我们可以说“调用对象X的Y方法”,而不能说“调用Y方法”。简单来说,方法和对象相关;而函数和对象无关。
因为Java是一门完全面向对象编程的语言,所以在Java中只有方法。
C语言white面向过程的编程语言,所以在C语言中只有函数。
方法是在类体中定义的
8.2 方法定义的语法机制
[修饰符列表] 返回值类型 方法名 ( 形式参数列表 ) {
方法体;
}
注:① [ ] 中的内容表示不是必须的,是可选的。
② 返回值类型
返回值可以是仍和Java中合法的类型,如果不需要返回值,则该地方要写void关键字。
有返回值的话可以使用变量来接收这个返回值,也可以不接收,只不过没有变量接收的话,该返回值的内存会马上释放。
③ return语句
若指定了返回值类型,则必须要写“return 值;” 这句语句,这个语句可以返回方法的执行结果,注意:“return 值;”这个语句执行的可能性要为1。
只要有return关键字语句的语句执行,当前方法必定结束。因此,在返回值类型为void的方法中,可以用“return;”这个语句来终止方法的执行。
在同一个域当中,return语句下面不能再编写代码。
④ 形式参数列表(形参)
形式参数列表的每一个参数都是局部变量,方法结束后内存释放。
形参的个数是0~n个。形参有多个的话使用英文逗号隔开。
⑤ 方法体
方法体由Java语句构成,遵循自上而下的执行顺序。
8.3 方法的调用
语法机制
类名.方法名(实际参数列表);
注:① 实参和形参要一一对应。
② 调用本类的方法时,“类名.”可以省略;跨类调用时,“类名.”不能省略。
代码实例:方法的定义以及调用
/*
方法的定义以及调用
*/
public class MethodTest01 {
public static void main(String[] args) {
int m = 100;
byte n = 10;
int result;
//调用本类的方法,没有返回值
MethodTest01.Sum1(m , n);
//调用本类中的方法可以省略类名
//使用result变量来接收返回值,并且打印
result = Sum2(m , n);
System.out.println(m + "+" + n + "=" + result);
/*错误:The method Method_A() is undefined for the type MethodTest01
Method_A();*/
//跨类调用时必须要添加类名
A.Method_A();
}
//定义一个本类中没有返回值的方法
public static void Sum1(int x, int y) {
int z = x + y;
System.out.println(x + "+" + y + "=" + z);
}
//定义一个本类中有返回值的方法
public static int Sum2(int x, int y) {
return x + y;
//在同一个域当中,return语句下面不能再编写代码,因为这个代码没有执行的机会,无意义
//错误:Unreachable code
//System.out.print("abc");
}
}
class A{
//定义一个在A类中的方法
public static void Method_A(){
System.out.println("This is A' method.");
}
}
代码实例:用户给输入一个正整数n,输出大于正整数n的最小的质数
/*
编写一个程序:用户给输入一个正整数n,输出大于正整数n的最小的质数
*/
public class MethodTest02 {
public static void main(String[] args) {
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请输入一个正整数:");
int n = s.nextInt();
// 使用for循环寻找n后的最小质数
for (; !judge(++n) ; ) {
}
System.out.println("输出大于正整数n的最小的质数是:" + n);
}
// 判断n是否为质数
public static boolean judge(int n) {
for (int m = 2 ; m <= n/2 ; m ++) {
if (n % m == 0) {
return false;
}
}
return true;
}
}
8.4 方法重载机制
作用:简化类似功能方法的引用。
发生方法重载的条件:
① 定义在同一个类中
② 方法名相同
③ 形式参数列表不同(数量不同/顺序不同/类型不同)
代码实例:方法重载机制
/*
方法重载机制
*/
public class OverloadTest {
public static void main(String[] args) {
M1();
M1(17 , 18.0F);
M1(18.0F , 17);
}
public static void M1() {
System.out.println("M1()");
}
public static void M1(int x , float y) {
System.out.println("M1(int x , int y)");
}
public static void M1(float x , int y) {
System.out.println("M1(float x , int y)");
}
}
8.7 方法递归
注:方法自己调用自己就是方法递归。
当递归没有结束条件时,会导致栈内存溢出错误。因此,递归必须要有结束条件。
能用循环实现就尽量不要使用递归。
发生栈内存溢出错误时的调试思路:
1.先检查结束条件
2.若结束条件没有问题,则调大栈的初始内存。
代码实例:使用递归计算1~n的和
/*
使用递归计算1~n的和
*/
public class RecursionTest {
public static void main(String[] args) {
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请输入一个正整数:");
int n = s.nextInt();
System.out.println(Sum(n));
}
public static int Sum(int i) {
if(i != 1) {
return i + Sum(i - 1);
}else {
return 1;
}
}
}
8.6 栈数据结构
8.6.1 JVM内存结构
参考资料链接: JVM内存结构.
8.6.2 栈数据结构
9、面向对象初步
C语言:面向过程
C++语言:一半面向过程,一半面向对象
Java语言:面向对象
9.1 OOA,OOD和OOP
OOA(Object-Oriented Analysis):面向对象分析
OOD(Object Oriented Designing):面向对象设计
OOP(Object Oriented Programming):面向对象编程
整个软件开发过程,都是采用OO贯穿的
9.2面向对象的三个特征
9.3 类和对象
类中描述的是所有对象的共同特征信息,对象(实例)是通过类创建的个体。就好比水果是一个类,苹果是一个对象。
类----(实例化)---->对象
对象----(抽象)----->类
类=属性+方法
9.3.1 类的定义
语法格式:
[ 修饰符列表 ] class 类名 {
类体 = 属性 + 方法;
}
注:① 修饰符列表可以省略
② 属性在代码上以变量的形式存在(成员变量)
③ 属性描述状态
④ 方法描述动作
⑤ 变量必须先声明,再赋值才能访问,但是成员变量没有手动赋值时,系统默认
代码实例:定义一个类
public class Student {
// 属性描述状态,在Java程序中以“成员变量”的形式存在
String name;//这种成员变量又被称为“实例变量”——对象级别的变量
String xueHao;
boolean sex;
String address;
int grades;
// 成员变量没有手动赋值时,系统默认赋值
}
9.3.2 对象的创建和使用
语法格式:
类名 变量名 = new 类名( );
这里 "new 类名();" 实际上是调用一个系统默认的无参构造方法,在这个方法中会给实例变量赋初始值。详情见
本章节9.6
类是模板,通过一个类,可以创建n个对象。
访问实例变量语法:
引用.实例变量;
注:① 通过“=”可以修改实例变量的值
② 实例变量有一个特点,只要它没有值,系统都会给它赋初始值,除非实例变量已经在所调用的构造方法中赋值了。
代码实例:结合9.3.1的代码,对象的创建和引用
/*
对象的创建与使用
*/
public class Object01 {
public static void main(String[] args) {
// s1这个局部变量叫“引用”,它存储的是对应对象在堆内存中的内存地址
Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();
// Student 是引用数据类型,以上new了3个对象,3个对象都属于学生类型
// 在堆内存中存储了对象
}
}
9.3.3 进阶套娃与内存图示意
代码1:地址信息类
/*
位置信息
*/
public class Address {
String city;
String street;
String zipcode;
}
代码2:用户信息类
/*
用户信息
*/
public class User {
String userName;
int age;
Address addr; //Address类的变量,存储指向Address类型对象的地址
}
代码3:套娃开始
/*
套娃开始
*/
public class TaoWa {
public static void main(String[] args) {
// new一个Address类型的对象,并将这个对象的地址保存到a变量
// a称为“引用”,可以通过a来访问对象
Address a = new Address();
a.city = "杭州市"; //引用.实例变量; 可以访问实例变量并且改变其值
a.street = "下沙街道";
a.zipcode = "310000";
User u = new User();
u.userName = "张三";
u.age = 25;
// 将a存储的Address类型的对象赋值给User类型的对象中的addr
// 这时addr也指向Address类型的对象,并且可以访问
// 换言之,addr也是引用
u.addr = a;
System.out.println("用户名:" + u.userName);
System.out.println("年龄:" + u.age);
System.out.println("住址" + u.addr.city + u.addr.street);
}
}
输出结果:
用户名:张三
年龄:25
住址杭州市下沙街道
内存图示意:
9.4 空指针异常
当引用的内容为空的话,就可能出现空指针异常(NullPointerException)的情况
代码实例:空指针异常
public class NullPointerException {
public static void main(String[] args) {
A a = new A();
a.i = 10;
System.out.println(a.i);//输出10
//报错:NullPointerException
a = null;
System.out.println(a.i);
}
}
class A {
int i;
}
9.5 方法调用时值的传递问题
代码实例:
public class Transfer {
public static void main(String[] args) {
int i = 10;
add_1(i);// 将i存储的值复制一份传递给add_1方法的形参i,这两个i的作用域不一样
System.out.println("main---->" + i);
Age a = new Age();
a.age = 20;
add_2(a);// 将a存储的地址复制一份传递给add_2方法的形参a,这两个a的作用域不一样
//但是这两个a指向的对象都是同一个,堆同一个对象进行操作
System.out.println("main---->" + a.age);
}
public static void add_1(int i) {
i++;
System.out.println("add_1---->" + i);
}
public static void add_2(Age a) {
a.age++;
System.out.println("add_2---->" + a.age);
}
}
class Age {
int age;
}
输出结果:
add_1---->11
main---->10
add_2---->21
main---->21
9.6 构造方法
通过构造方法可以完成对象的创建,以及实例变量的初始化。
当一个类没有提供构造方法时,系统会默认提供一个无参数的构造方法,而这个构造方法被称为缺省构造器。
9.6.1 调用构造方法
使用new运算符来调用构造方法。
语法格式:
new 构造方法名(实际参数列表);
9.6.2 构造方法的语法格式
[ 修饰符列表 ] 构造方法名 (形式参数列表){
构造方法体;
}
注:① 构造方法名和类名必须一致。
② 构造方法不需要指定返回值。
③ 通常在构造方法体当中给属性赋值,完成属性的初始化。
④ 构造方法支持方法重载。
9.7 实例方法
没有用static关键字修饰的方法。实例方法是对象级别的方法。
9.7.1 如何调用实例方法
实例方法是对象级别的方法,要调用实例方法要先创造一个对象,通过对象去调用。
语法格式:
引用.实例方法名();
9.8 代码实战
代码要求:
定义丈夫类Husband和妻子类wife,
丈夫类的属性包括:身份证号,姓名,出生日期,妻子。
妻子类的属性包括:身份证号,姓名,出生日期,丈夫。
分别给这两个类提供构造方法(无参数构造方法和有参数构造方法都要提供),编写测试程序,创建丈夫对象,
然后再创建妻子对象,丈夫对象关联妻子对象,妻子对象关联丈夫对象,要求能够输出这个"丈夫对象"的妻子的名字,
或者能够输出这个"妻子对象"的丈夫的名字。
并且要求在程序中演示出空指针异常的效果
public class Husband_Wife {
public static void main(String[] args) {
Husband h = new Husband("123456798","张三","19970202");
Wife w = new Wife("147258369","李华","199970317");
h.wife = w;
w.husband = h;
System.out.println(h.name + "妻子的名字是:" + h.wife.name);//丈夫的妻子的名字
System.out.println(w.name + "丈夫的名字是:" + w.husband.name);//妻子的丈夫的名字
}
}
class Husband{//丈夫类
//身份证号
String id;
//姓名
String name;
//出生日期
String barth;
//妻子
Wife wife;
public Husband() {//无参数构造方法
}
public Husband(String s1,String s2,String s3) {//有参数构造方法
id = s1;
name = s2;
barth = s3;
}
}
class Wife{//妻子类
//身份证号
String id;
//姓名
String name;
//出生日期
String barth;
//丈夫
Husband husband;
public Wife() {//无参数构造方法
}
public Wife(String s1,String s2,String s3) {//有参数构造方法
id = s1;
name = s2;
barth = s3;
}
}
运行结果:
张三妻子的名字是:李华
李华丈夫的名字是:张三
十、封装
10.1 封装的作用
① 保证内部结构的安全
② 屏蔽复杂,暴露简单
一个类体中的数据如果封装之后,对于代码的调用人员来说,不需要关心其中代码的复杂实现,只需要一个简单的入口就可以访问了。另外,将类体中安全级别较高的数据封装起来,外部人员不能随意访问,就保证了数据的安全。
10.2 get方法和set方法
get方法的规范:
public 返回值类型 get属性名(无参数){
return 值;
}
注:属性名首字符要大写
set方法规范
public void set属性名(参数){
属性值=参数;
}
注:属性名首字符要大写
10.3 封装的步骤
step1 属性私有化
step2 对外提供方法来访问属性,可以在方法中设置关卡来保证数据的安全性。
封装的代码实例:
public class Encapsulation {
public static void main(String[] args) {
Student s1 = new Student();
/*
getAge和setAge均为实例方法,是一个入口
这两个方法都是对象级别的方法
要调用这两个方法要先创建对象,再通过"引用.方法名();"调用
*/
System.out.println(s1.getAge());//0
s1.setAge(20);
System.out.println(s1.getAge());//20
}
}
class Student {
private int age;
// setAge方法用来修改age变量的值
public void setAge(int a) {
if (a < 0 || a > 150) {//方法入口设立关卡
System.out.println("输入的年龄不合法");
}
age = a;
}
// getAge方法用来读取age变量的值
public int getAge() {
return age;
}
}
运行结果:
0
20
十一、static和this
11.1 static关键字
static是一个修饰符,用于修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
11.2 静态变量
加static的变量叫做静态变量是类级别的,静态变量在类加载时初始化,静态变量存储在方法区。
11.2.1 什么时候使用静态变量
当该变量是这个类型的所有对象所共有的,且其不随对象的改变而改变时,则其可以声明为静态变量。静态变量在方法区中只保留一份,可以节省内存空间。例如:国籍的中国人类所共有的,且所有对象都是一样的。
11.2.2 访问静态变量
语法格式:
类名.静态变量;
注:空引用静态变量不会空指针
代码实例:静态变量
/*
静态变量
*/
public class StaticTest01 {
public static void main(String[] args) {
Chinese c1 = new Chinese("123456789","李四");
System.out.println(c1.idCard);
System.out.println(c1.name);
System.out.println(Chinese.nationality);// 静态变量可以通过"类名."访问
System.out.println(c1.nationality);// 静态变量也可以通过"引用."访问
c1 = null;
System.out.println(c1.nationality);// 空引用访问静态变量不会空指针错误
}
}
class Chinese{
String idCard;
String name;
static String nationality = "中国";
public Chinese(String s1, String s2) {
idCard = s1;
name = s2;
}
}
运行结果:
123456789
李四
中国
中国
中国
11.3 静态方法
11.3.1 静态方法的调用
语法格式:
类名.实例方法名(实参列表);
11.3.2 静态方法与实例方法区别
静态方法由static关键字,而实例方法没有。使用静态方法不需要创建对象,使用实例方法需要创建对象。在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
代码实例:静态方法
/*
静态方法
*/
public class StaticTest03 {
public static void main(String[] args) {
P.print1();//静态方法的调用直接使用"类名.",不需要创建对象
P a = new P();//调用实例方法需要创建对象
a.print2();//使用"引用."调用实例方法
a.print1();//使用"引用."也可以调用静态方法
a = null;
a.print1();//空引用静态方法不会空指针
}
}
class P {
String s;
public static void print1() {
System.out.println("静态方法执行!");
}
public void print2() {
System.out.println("实例方法执行!");
System.out.println("s的内容为:" + s);
}
}
运行结果:
静态方法执行!
实例方法执行!
s的内容为:null
静态方法执行!
静态方法执行!
11.4 静态代码块
11.4.1 静态代码块的语法格式
static{
java语句块;
}
11.4.2 静态代码块特点
在类加载的时候执行,并且只执行一次。在main方法前执行,执行顺序一般是自上而下。一个类当中可以编写多个静态代码块。
Java提供一个了类加载时机,静态代码块可以在类加载时机时执行。
代码实例:静态代码块
/*
静态代码块
*/
public class StaticTest02 {
static {
System.out.println('A');
}
static {
System.out.println('B');
}
public static void main(String[] args) {
System.out.println("main");
}
static {
System.out.println('C');
}
static int i = 100;
static {
System.out.println("i = " + i);//在静态代码块中可以访问静态变量
}
}
运行结果:
A
B
C
i = 100
main
11.4.3 对比:实例代码块
语法:
{
java语句块;
}
实例代码块的特点
Java在构造方法执行之前,会自动执行“实例代码块”中的代码
代码实例:实例代码块
/*
实例代码块
*/
public class Instance {
public static void main(String[] args) {
new Instance();
}
public Instance(){
System.out.println("构造方法执行");
}
{
System.out.println("实例代码块执行!");
}
}
运行结果:
实例代码块执行!
构造方法执行
11.5 this关键字
this是一个变量,是一个引用。this保存当前对象的地址,指向自身。所以,严格意义上来说,this代表的就是“当前对象”。this存储在堆内存当中对象的内部。
11.5.1 this不能省略的情况
在实例方法和构造方法在,用于区分局部变量和实例变量。
代码实例:
/*
在实例方法和构造方法在,this用于区分局部变量和实例变量
*/
public class ThisTest01 {
public static void main(String[] args) {
Student s1 = new Student();
s1.setNo("123");
s1.setName("张三");
System.out.println("姓名:" + s1.getName());
System.out.println("学号:" + s1.getNo());
}
}
class Student{
//学号
private String no;
//姓名
private String name;
public Student() {
}
public Student(String no,String name) {
this.no = no;
this.name = name;
}
public void setNo(String no) {
this.no = no;//用于区分局部变量no和实例变量no
}
public String getNo() {
return no;
}
public void setName(String name) {
this.name = name;//用于区分局部变量name和实例变量name
}
public String getName() {
return name;
}
}
运行结果:
姓名:张三
学号:123
11.6 this()语句
语法格式:
this(实际参数列表);
作用:通过当前的构造方法去调用另一个本类的构造方法,做到代码复用。
注:对this()的调用只能出现在构造方法的第一行。
代码实例:
/*
this()语句的使用
*/
public class ThisTest02 {
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1.getYear() + "年" + d1.getMonth() + "月" + d1.getDay() + "日");
}
}
class Date{
//年
private String year;
//月
private String month;
//日
private String day;
//无参数构造方法,使得时间的默认值为1970年1月1日
public Date() {
this("1970","01","01");
}
public Date(String year,String month,String day) {
this.year = year;
this.month = month;
this.day = day;
}
//set方法和get方法
public void setYear(String year) {
this.year = year;
}
public String getYear() {
return year;
}
public void setMonth(String month) {
this.month = month;
}
public String getMonth() {
return month;
}
public void setDay(String day) {
this.day = day;
}
public String getDay() {
return day;
}
}
运行结果:
1970年01月01日
十二、继承(extends)
12.1 继承的作用
基本作用:子类继承父类,代码可以得到复用。
主要作用:有了继承关系,才有了方法的覆盖和多态机制。
12.2 继承的语法格式
class 类名 extends 父类名 {
类体;
}
12.3 继承的特性
① B类继承A类,则称A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、派生类、扩展类。
② java中的继承只支持单继承,不支持多继承,C++中支持多继承,这也是java体现简单性的一点,换句话说,java中不允许这样写代码: class B extends A,C{ }。
③虽然 java中不支持多继承,但有的时候会产生间接继承的效果,例如: class C extends B,class B extends A,也就是说,C直接继承B,其实C还间接继承A。
④ java中规定,子类继承父类,除构造方法不能继承外,剩下都可以继承。但是父类中private数据无法在子类中直接访问,可以间接访问。
⑤ java中的类没有显示的继承任何类,则默认继承Object类,Object类是java语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有Object类型中所有的特征。
⑥ 继承也存在一些缺点,例如:CreditAccount类继承Account类会导致它们之间的耦合度非常高,Account类发生改变之后会马上影响到CreditAccount类。
代码实例:继承
public class ExtendsTest01 {
public static void main(String[] args) {
Class_1 s1 = new Class_1();
s1.setStudentName("李四");
s1.setStudentNo("147258");
System.out.println(Student.grade + "年级1班的" + s1.getStudentName() + "同学的学号是:" + s1.getStudentNo());
}
}
class Student{//学生信息
private String studentName;
private String studentNo;
static int grade = 1;
public Student() {//无参数构造
}
public Student(String studentName,String studentNo) {//有参数构造
this.studentName = studentName;
this.studentNo = studentNo;
}
//set方法和get方法
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentName() {
return studentName;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentNo() {
return studentNo;
}
}
class Class_1 extends Student{//班级1的学生
public Class_1() {//无参数构造
}
}
运行结果:
1年纪1班的李四同学的学号是:147258
十三、方法覆盖和多态
13.1 为什么要进行方法覆盖
子类在继承父类的时候,当继承过来的方法不能满足要求时,子类有权利对这个方法进行重新编写,就是方法覆盖。
13.2 构成方法覆盖的条件
① 两个类必须要有继承关系。
② 重写之后的方法要有与原来的方法相同的返回值,相同的方法名,相同的形式参数列表。
③ 重写之后的方法访问权限不能更低,可以更高。
④ 重写之后的方法不能比之前的方法抛出更多的异常,可以更少。
注:① 方法覆盖只是针对于方法,和属性无关。
② private方法无法覆盖。
③ 构造方法不能被继承,因此也不能覆盖。
④ 方法覆盖只针对实例方法,静态方法覆盖没有意义
代码实例:方法覆盖
/*
方法覆盖
*/
public class OverrideTest {
public static void main(String[] args) {
Dog d = new Dog();
d.act();
}
}
class Animal{
private String name;
//构造方法
public Animal() {
}
public Animal(String name) {
this.name = name;
}
//set方法和get方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void act() {
System.out.println("动物在运动");
}
}
class Dog extends Animal{//方法覆盖只发生在有继承关系的父类和子类的子类中
public Dog() {
}
public void act() {//方法覆盖
System.out.println("小狗在跑");
}
}
运行结果:
小狗在跑
13.3 多态(polymorphism)
多态是指多种形态,多种状态,编译和运行有两个不同的状态。
编译期叫做静态绑定。运行期叫做动态绑定。
多态的典型代码:父类型的引用指向子类型的对象。
13.4 向上和向下转型
① 向上转型
父类型的引用允许指向子类型的对象。
② 向下转型
父类转换成子类。
注:① 无论是向上转型还是向下转型,两种类型之间必须要有继承关系。
② 需要调用或者执行子类中特有的方法,必须要进行向下转型,才可以调用。
③ 向下转型会出现类型转换异常(java.lang.ClassCastException)
代码实例:向上转型与向下转型
public class PolymorphismTest01 {
public static void main(String[] args) {
Animal a1 = new Animal();
Animal a2 = new Cat(); // 向上类型转换
Animal a3 = new Bird(); // 向上类型转换
a1.move();
a2.move();
a3.move();
Cat c1 = (Cat) a2; // 向下类型转换
c1.catAct(); // 要调用子类特有的方法时,必须要用向下类型转换
Bird b1 = (Bird) a3;
b1.birdAct();
}
}
class Animal {
public void move() {
System.out.println("动物在移动");
}
}
class Cat extends Animal {//Cat类继承Animal类
public void move() {//方法覆盖
System.out.println("猫在跑!");
}
public void catAct() {//猫类特有的方法
System.out.println("猫在抓老鼠");
}
}
class Bird extends Animal {//Bird类继承Animal类
public void move() {//方法覆盖
System.out.println("鸟在飞!");
}
public void birdAct() {//鸟类特有的方法
System.out.println("鸟在唱歌");
}
}
运行结果:
动物在移动
猫在跑!
鸟在飞!
猫在抓老鼠
鸟在唱歌
13.5 类型转换异常(java.lang.ClassCastException)
出现错误的原因:
无论是向上转型还是向下转型,两种类型之间必须要有继承关系。
可以使用instanceof运算符避免
代码实例:ClassCastException
public class ClassCastException {
public static void main(String[] args) {
Animal a1 = new Animal();
Animal a2 = new Cat(); // 向上类型转换
Animal a3 = new Bird();
Bird b1 = (Bird) a2;
}
}
class Animal {
public void move() {
System.out.println("动物在移动");
}
}
class Cat extends Animal {
public void move() {//方法覆盖
System.out.println("猫在跑!");
}
public void catAct() {//猫类特有的方法
System.out.println("猫在抓老鼠");
}
}
class Bird extends Animal {
public void move() {//方法覆盖
System.out.println("鸟在飞!");
}
public void birdAct() {//鸟类特有的方法
System.out.println("鸟在唱歌");
}
}
运行结果:
Exception in thread "main" java.lang.ClassCastException: class Cat cannot be cast to class Bird (Cat and Bird are in unnamed module of loader 'app')
at ClassCastException.main(ClassCastException.java:7)
13.6 instanceof运算符
作用:instanceof运算符可以在运行阶段动态判断引用指向的对象的类型。
instanceof运算符的语法
(引用 instanceof 类型)
返回结果:instanceof运算符的运算结果只能是true/false
代码实例:使用instanceof运算符避免类型转换异常
public class PolymorphismTest01 {
public static void main(String[] args) {
Animal a1 = new Animal();
Animal a2 = new Cat(); // 向上类型转换
Animal a3 = new Bird(); // 向上类型转换
a1.move();
a2.move();
a3.move();
//instanceof运算符的返回值类型只能是true/false
System.out.println((a2 instanceof Cat));
System.out.println((a2 instanceof Bird));
//使用instanceof运算符避免类型转换异常问题
if (a2 instanceof Cat) {
Cat c1 = (Cat) a2; // 向下类型转换
c1.catAct();
} else if (a2 instanceof Bird) {
Bird b1 = (Bird) a2;
b1.birdAct();
}
if (a3 instanceof Cat) {
Cat c1 = (Cat) a3; // 向下类型转换
c1.catAct();
} else if (a3 instanceof Bird) {
Bird b1 = (Bird) a3;
b1.birdAct();
}
}
}
class Animal {
public void move() {
System.out.println("动物在移动");
}
}
class Cat extends Animal {//Cat类继承Animal类
public void move() {//方法覆盖
System.out.println("猫在跑!");
}
public void catAct() {//猫类特有的方法
System.out.println("猫在抓老鼠");
}
}
class Bird extends Animal {//Bird类继承Animal类
public void move() {//方法覆盖
System.out.println("鸟在飞!");
}
public void birdAct() {//鸟类特有的方法
System.out.println("鸟在唱歌");
}
}
运行结果:
动物在移动
猫在跑!
鸟在飞!
true
false
猫在抓老鼠
鸟在唱歌
13.7 多态在开发中的作用
编写一个高层抽象的类,便于扩展。降低程序的耦合度,提高程序的扩展力。
十四、super
14.1 super关键字
super代表的是“当前对象”的那部分父类型特征。
14.2 super注意事项
super应当和this对照起来学习。
super能出现在实例方法和构造方法中
super的语法是:“super.”和“super();”
super不能使用在静态方法当中
“super.”在大部分情况下是可以省略的
“super();”只能出现在构造方法的第一行,通过当前的构造方法去调用父类中的构造方法,目的是:创建子类对象时,先初始化父类特征。
super的使用方法:
super.属性名;
super.方法名(实参列表);
super();
14.3 "super."不能省略的情况
当父类和子类中有一个同名的属性或方法时,想在子类中访问父类的属性或方法,“super.”就不能省略。
代码实例:"super."用于调用父类的属性与方法
/*
"super."用于调用父类的属性与方法
*/
public class SuperTest02 {
public static void main(String[] args) {
Human h1 = new Student("张三");
h1.act();
}
}
class Human{
String name;
//构造方法
public Human() {
}
public Human(String name) {
this.name = name;
}
//父类的act方法
public void act() {
System.out.println("Human类的act方法执行!");
}
}
class Student extends Human{
String name;
//构造方法
public Student() {
}
public Student(String name) {
this.name = name;
}
public void act() {
System.out.println("Student类的act方法执行!");
super.act();//调用父类的方法
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);//输出父类的属性
}
}
输出结果:
Student类的act方法执行!
Human类的act方法执行!
张三
张三
null
14.4 "super();"的使用
表示通过子类的构造方法去调用父类的构造方法。
注:① 当一个构造方法的第一行既没有“this();”也没有“super();”时,会默认有一个“super();” 因此必须保证父类中要有无参数构造方法。
② “this;”和“super;”不能共存,因为他们都出现在构造方法的第一行。
代码实例:"super();"的使用
/*
super();
*/
public class SuperTest01 {
public static void main(String[] args) {
new C();
}
}
class A{
public A() {
//这里也会默认有super(); 调用老祖宗类Object的无参构造
System.out.println("A的无参构造");
}
public A(int i) {
}
}
class B extends A{
public B() {
}
public B(int i) {
//这里没有写super();和this();
//默认会有一个super(); 调用父类A中的无参构造
System.out.println("B的有参构造(int)");
}
}
class C extends B{
public C() {
this(1);// 调用本类中有一个int参数的构造方法
System.out.println("C的无参构造");
}
public C(int i) {
this(i,1.2);
System.out.println("C的有参构造(int)");
}
public C(int i,double j) {
super(5);// 调用父类B中的有一个int参数的有参构造
System.out.println("C的有参构造(int,double)");
}
}
运行结果:
A的无参构造
B的有参构造(int)
C的有参构造(int,double)
C的有参构造(int)
C的无参构造
14.5 "super(实参);"的使用
调用父类中形式参数列表与传递进"super(实参);"的实际参数列表一致的构造方法
代码实例:"super(实参);"的使用
/*
"super(实参);"的使用
*/
public class SuperTest03 {
public static void main(String[] args) {
Pet xiaoWnag = new Dog("小汪");
}
}
class Pet{
String name;
//构造方法
public Pet() {
}
public Pet(String name) {
this.name = name;
System.out.println("Pet类有参构造方法执行!");
}
}
class Dog extends Pet{
//构造方法
public Dog() {
}
public Dog(String name) {
super(name);//这里是调用Pet类有一个String类型的参数的构造方法
System.out.println("Dog类有参构造方法执行!");
}
}
运行结果:Pet类有参构造方法执行!
Dog类有参构造方法执行!