java期末复习
期末复习
第一章 Java概述
-
Java语言:Java语言是一种 面向对象 的 高级 程序设计语言。
-
Java语言特点:java 具有语法简单,面向对象,稳定,与平台无关,多线程,动态等特点
-
java虚拟机(JVM)
-
无论哪种Java开发平台都需要具有Java虚拟机 (Java Virtual Machine)
-
Java虚拟机的核心是所谓的字节码指令
-
字节码是可以被java虚拟机直接识别,执行的一种由 0,1组成的序列代码。
-
字节码不是机器指令,因为他不和特定的平台相关,不能被任何平台直接识别,执行
-
java针对不同平台提供的java虚拟机的字节码指令都是相同的,因此能够实现平台无关性,例如所有的虚拟机都将11110000识别,执行为加法操作。
-
-
与c/c++不同,java语言提供的编译器不针对特定的操作系统和cpu芯片进行编译,而是针对虚拟机font>把java源程序编译程被称为“字节码”(能够被虚拟机识别的)的“中间代码”。
例如:,Java源文件中的+被编译成字节码指令111000字节码是可以被Java虚拟机识别、执行的代码,即Java虚拟机负责解释运行字节码,其运行原理是:Java虚拟机负责将字节码翻译成虚拟机所在平台的机器码,并让当前平台运行该机器码,如下图所示。
在一个计算机上编译得到的字节码文件可以复制到任何一个安装了Java运行环境的计
算机上直接使用。字节码由Java虚拟机负责解释运行,即Java虚拟机负责将字节码翻译成
本地计算机的机器码,并将机器码交给本地的操作系统运行 -
虚拟机负责将字节码文件(包括程序使用的类库中 的字节码)加载到内存,然后采用解释方式来执行字节码文件。
- 运行Java程序
- 屏蔽底层(操作系统、硬件)的差异性
- 解释执行
-
-
java运行机制:
题
1.
答案:B
解析:Java语言程序翻译成低级语言程序是.class中间字节码文件,不能直接执行,需要JVM去解释执行。
2.对下面的Java程序,试判断对错: 程序中有多个main方法,编译器无法找到执行入口,所以程序有错误。
class PersonA {
void speakHello() {
System.out.println("nice to meet you");
}
public static void main(String args[]) {
System.out.println("you are welcome");
}
}
public class PersonB {
void speakHello() {
System.out.println("欢迎你");
}
}
class TestPerson {
public static void main(String args[]) {
System.out.println("你好");
}
}
答案:错误
解析:Java程序编译后,每个类产生各自独立的.class文件,执行时互不影响。
-
关于Java语言程序,下列说法错误的是:D
运行Java程序的命令是java.exe。
-
关于Java开发工具包,下列说法正确的是:A
JDK、JRE、JVM这三者的关系是JDK包含JRE,JRE包含JVM。
-
关于Java类,下列说法错误的是:D
一个Java程序可以有一个主类,也可以没有。
-
关于main方法,下列说法错误的是:
若一个Java程序不是用来运行的(不是Java应用程序),可以没有main方法。
-
对下面java程序
class PersonA { void speakHello() { System.out.println("nice to meet you"); } public static void main(String args[]) { System.out.println("you are welcome"); } } public class PersonB { void speakHello() { System.out.println("欢迎你"); } } class TestPerson { public static void main(String args[]) { System.out.println("你好"); } }
虽然PersonA不是public的类,但是类PersonA语法正确,又包含main方法,可以运行。
第二章 基本程序设计
-
java数据类型
-
数据类型转换
转型范围排序:double>float>long>int
隐式转型:小范围可给大范围赋值
int a=10; double b=a; System.out.println(b); //结果10.0
显式转型:大范围转成小范围 需要强制转型
double b=10.0; int a=(int)b; System.out.println(a); //结果10
-
标识符
- 定义:
标识符是用来标识变量、常量、方法、类、对象等元素的 有效字符序列。
-
命名规则:
-
标识符是一个由字符、数字、下划线( _ )或美元符号($)构成 的字符串
-
标识符必须以字符、下划线(_)或美元符号($)开头,不能用 数字开头
-
标识符不能是Java保留字 和 true、false、null等字面常量
-
标识符可以有任意的长度,但实际命名不宜过长
-
-
数值运算
-
整数与整数运算,结果为整数
-
整数与浮点数运算,结果为浮点数
-
两数取余时,只有当被除数是负数时,余数才是负的 (其它情况均为正)
例:5%2 = 1 5%-2 = 1 -5%2 = -1 -5%-2 = -1
-
-
变量
- 变量用于表示在程序运行过程中其值可以改变的量
- 变量必须先定义后使用
- 声明变量(变量名、数据类型)
- 赋值(分配存储空间)
- 初始化:声明变量的同时还可以对变量赋值
-
赋值表达式
-
实例
-
下代码 错误 y=z=1是运算表达式是将预算表达式赋给x,只有x进行了声明,yz均未
int x = y = z = 1 ;
-
下代码,xyz均已声明 正确
int y , z ; int x = y = z = 1 ; //或 int x , y , z ; x = y = z = 1 ;
-
-
题
-
下列语句中,正确地定义了变量x并为其赋值的是:CD
-
下列赋值语句中,错误的是:BD
B:大范围不能隐式给小范围赋值,需要强制转型
C:long<float可以隐式赋值
D:12E3为double型 大范围不能隐式给小范围赋值,需要强制转型
-
下列表达式中,值为-2的是:BC
两个数取余时,只有被除数为负数余数才为负数
-
若x的值是135.26847,下列表示式运算后,x值为135.27的是:BD
要四舍五入,因此x要先加上0.005;
要保留小数点后面两位,因此先乘以100,取整(截去小数部分),再除以100.0。
-
下列对char类型变量的定义,正确的是:
- 字符类型常量必须用一对单引号’'括起来;
- 只能表示单个字符;
- 可以是转义字符;
- 可以用’\u’加一个十六进制的Unicode值,Unicode值为4位数字。
第三章 选择
-
java运算符优先级
-
if语句
-
注意1:
-
注意2:
-
else 和 if 的匹配问题
else子句与同一块中离得最近的if子句相匹配
注意:因为嵌套的if-else都只有一句所以才在if(i>j)的范围
-
-
逻辑运算符
-
Switch
题
-
下列语句执行时可以输出“welcome”的是:C
-
已知整型变量i,j,k,对下面的程序段:
if (i > j) System.out.println('A'); if (i > k) { System.out.println('B'); } else { System.out.println('C'); }
当i,j,k的值可以分别是多少时,程序可以将“C”输出:BD
if(i>j)下面已经有一句sys,因为if(i>j)没有{}所以后面的if-else不在if(i>j)的范围中,以只要 i>k 为假即可。
-
当|x-3|>1.5时y赋值为1,否则y赋值为2,下面哪些为正确的if语句:C
-
与下列程序段等价的是:C
switch(x+1){ case 1: y = 1; break; case 2: y = 2; default: y += 1; }
第四章 数学函数,字符,字符串
-
三角函数
-
指数函数
-
取整函数
-
随机值
-
Math.random():
- 获取区间[a, b) 内的一个随机整数:
(int)( a + Math.random( ) *(b - a) )
- 获取区间 a ~ b 之间的一个随机整数:
(int)( a + Math.random( ) *(b – a + 1) )
-
random.next…():
- 获取区间[a, b) 内的一个随机整数:
a+ran.nextInt(b-a)
- 获取区间 a ~ b 之间的一个随机整数:
a+ran.nextInt(b-a+1)
-
-
Character类字符判别方法
字符型在打印或参数数学运算都会以ASCII的形式
-
字符型通常计算机采用ASCII码(8位编码),Java支持 Unicode编码(16位编码)
-
连接字符串
- 字符串直接相加 (用"+")
String str1 = "Hello"; String str2 = "World"; String str3 = str1 + str2;//str3: "HelloWorld"
str1的值不变
- 使用 String 的 concat 方法
String str1 = "Hello"; String str2 = "World"; String str4 = str1.concat(str2); //str4: "HelloWorld"
str1的值不变
- 多个字符串相加,支持连写:
String str5 = str1 + str2 + str3 + str4; String str6 = str1.concat(str2).concat(str3).concat(str4);
-
字符串的大小写转换
一旦字符串被创建之后它的内容就不能改变。可以使用字符串转换方法将字符串转换为新字符串(但是原来的不变)
-
字符串的比较方法
注意:ComparaTo只比较到可比较的第一个字符 后面的不管
例如:
String str1 = new String("Hello,Wo"); String str2 = "Hello,wO"; System.out.println(str1.compareTo(str2));
结果为:-32
str1<str2 即使w后的o大于O依旧不管甚至多几个字母结果依然是-32
-
获取,查找,替换子字符串
-
获取
-
public String substring( int beginIndex , int endIndex )
注: beginIndex: 开始点的索引,包含本字符(索引起始为0) endIndex: 结束点的索引,不包含本字符 得到从 beginIndex 到 endIndex - 1 之间的子串。
-
public String substring( int beginIndex )
注: beginIndex: 开始点的索引,包含本字符 得到从 beginIndex 开始直到字符串结束的字
-
-
查找
注意:
indexof(int ch)的ch是ASCII码或Unicode:
例如:
String s="01234"; System.out.print(s.indexOf(48)); System.out.print(s.indexOf(49)); System.out.print(s.indexOf(50));
输出:012(是索引)。
-
替换
-
-
数字字符串与数值间的转换
-
数字字符串转换为数值
- 数字字符串 -> int
Integer. parseInt(String)
- 数字字符串 -> float
Float. parseFloat(String)
- 数字字符串 ->double
Double. parseDouble(String)
-
数值转换为数字字符串
- 将数值与一个空字符串连接
20.5 + "" ; "" + 20.5 ;// 得到"20.5"
- 用 String 类中的 valueOf 方法
String.valueOf( a ),a 可以是任意的数值 、字符 或 字符串
String value1 = String.valueOf(12); // int ->String:"12" String value2 = String.valueOf(12.0); // double -> String:"12.0" String value3 = String.valueOf('9'); // char -> String:"9" String value3 = String.valueOf('c'); // char -> String:"c"
-
题
-
下列表达式中,可以表示一个随机大写字符的是:BD
-
下面的表达式中,能得到字符串“5Hello5”的是:CD
-
下列语句输出为:D
第五章 循环
-
for语句执行过程示意图
题
-
下面程序段的输出是:C
int i = 0,sum = 1; do { sum = sum * i; i++; } while( i < 5 ); System.out.println(sum + "," + i);
-
与下面程序段的语义等价的程序段是:A
int a, b; for(a=1,b=5;b-->0;a++);
A:
int a,b; a=1; b=5; while(b>0){ b--; a++; }
B:
int a,b; a=1; b=5; do{ b--; a++; }while(b>0);
C:
int a,b; a=1; b=5; while(b>0){ a++; b--; }
D:
int a,b; a=1; b=5; do{ a++; b--; }while(b>0);
四个选项虽然结果都一样,但是原循环体是先b–,后再a++,而CD顺序错了
-
设a、b为任意的非0自然数,下面程序段中,可以输出a和b的最小公倍数的是:A
A:
int x; for(x=a; ;x++) if(x%a==0 && x%b==0) break; System.out.println(x);
B:
int x; for(x=a;x%a==0 && x%b==0;x++) break; System.out.println(x);
X是a,b的最小公倍数才能进入循环
C:
int x; x = b; while(x%a!=0 && x%b!=0) x++; System.out.println(x);
x必须对两个数取余都不能为0才能进入循环,但是x=b,x%b是对自己取余必定为0,所以无法进入循环
D:
int x; x = b; do{ x++; }while(x%a!=0 && x%b!=0); System.out.println(x);
如果循环一开始就满足题目要求,则x跳过去了,不能得到最小公倍数,如4和8。
-
与下面程序段输出结果相同的是:A
int sum = 0; for(int i = 0; i <= 6; i++){ if( i % 3 == 0 ) continue; sum += i; } System.out.println(sum);
A.
int i = 0, sum = 0; while( i <= 6 ){ if( i % 3 == 0 ){ i++; continue; } sum += i; i++; } System.out.println(sum);
if中必须有i++,否则会跳过当前循环导致无法i++,一直卡死。
B.
int i = 0, sum = 0; while( i <= 6 ){ if( i % 3 != 0 ) break; sum += i; i++; } System.out.println(sum);
选项B只把i=0加入到sum中,当i=1时跳出了循环
C.
int i = 0, sum = 0; while( i <= 6 ){ i++; if( i % 3 == 0 ) continue; sum += i; } System.out.println(sum);
当i=6是,进入循环会把7加入sum,多加一个7变为19。
-
已知一个正整数n,下面程序段中可以从小到大输出n的所有因子(可以重复)的是:A
(比如对于120,输出2,2,2,3,5)
A.
int i = 2; while (i <= n) { if (n % i == 0) { n /= i; System.out.println(i); } i++; }
120的因数有3个2,此代码得到一个2后就会继续遍历,无法得到多个2
B.
int i = 2; while (i <= n) { if (n % i == 0) { n /= i; System.out.println(i); } else i++; }
只要n/2 ==0就不会进入else分支,也就不会继续遍历,所以可将120的三个2都得到
第六章 方法
-
方法的格式
方法头:指方法修饰符、返回值类型、方法名、方法的参数(包括方法签名)。
方法签名:指方法名称和参数列表(参数的类型、个数、顺序)
参数传递
-
值传递:在调用带参数的方法时,实参的值赋给形参,这称为值传递。
-
参数传递要点:
- 实参必须与方法声明中的形参在顺序上和数量上匹配
- 实参必须与方法声明中的形参在类型上兼容
- 值传递:在调用带参数的方法时,实参的值传递给形参
-
在类型上兼容: 类型兼容是指不需要经过显式的类型转换,实参的值就可以传递给形参。
例如:可将int型的实参值传递给int、long 或 double型等形参
public class test { public static void main(String[] args) { int x = 10; int y = 13; double maxNumber = max(x, y);//!!!!!!!!这里 System.out.printf("The max mum between %d and %d is %d\n", x, y, maxNumber); } static double max(double n1, double n2) { double result; if (n1 >= n2) result = n1; else result = n2; return result; } }
-
按值传递:无论形参在方法中如何改变,实参不受影响
如下代码:
public class test { public static void main(String[] args) { int x = 1; int y = 2; System.out.printf("交换前:x = %d, y = %d\n" , x, y); swap(x, y); System.out.printf("交换后: x = %d, y = %d\n" , x, y); } public static void swap ( int n1, int n2 ) { System.out.printf("\t方法内交换:\n"); System.out.printf("\t\tswap交换前:n1 = %d,n2 = %d\n", n1, n2); int temp = n1; n1 = n2; n2 = temp; System.out.printf("\t\tswap交换后:n1 = %d,n2 = %d\n", n1, n2); } }
结果:
原因解释:
-
swap 对实参没有交换成功,这是因为在 Java 中, 方法调用是 “通过值传递参数的”
-
在方法调用时,实参的值,被copy给形参,形参并没有指向实参的地址,形参有自己的地址。
-
实参与形参占据不同的内存空间。因此,在方法内部改变形参的值,并不会影响到实参的值。
-
堆栈的方式图解:
重载方法
-
如果一个类中存在多个方法的名字相同,而这些方法的参数列表(参数的类型,顺序,个数)不同,这种现象称为重载。
-
参数列表不同:参数个数不同、或参数类型不同、 或两者都不同。
-
为了可读性,重载的方法功能相同(方法名相同)
-
调用方法时,Java编译器寻找最精确匹配的方法进行调用。
-
不能基于不同修饰符或返回值类型来重载方法
-
歧义调用:有时一个方法调用会有两个或更多可能的匹配,编译器无法判断哪个更为合适。歧义调用会产生编译错误,考虑如下方法定义:
max ( int , double ) max ( double , int ) 当主程序中有以下调用: max ( 1 , 2 ) 由于两个方法谁也不比谁更合适,都可能调用匹配。 所以这个调用是有歧义的,导致编译错误。
题
-
下面对Java语言的方法的说明,错误的是:BC
-
对于下面的方法的调用,在main方法执行完毕时的输出是:5 12 5
public static void main(String[] args){ int n = 5; System.out.println(n); method(n); System.out.println(n); } public static void method(int n){ for(int i=1; i<n; i++ ){ System.out.print(i + " "); n--; } System.out.println(); }
-
对于方法
public static double get(int n, double m){
//…
}
下面方法哪些不是该方法的重载:AB
方法重载:一个类中方法名相同、参数列表不同,与返回值类型或修饰符无关。AB的参数列表没有变不是重载。
-
对下面方法,说法正确的是:BC
public static void method(int k){ int n=5, sum=0; for(int i=1; i<n; i++ ){ sum += i ; } System.out.println("i=" + i + ",sum=" + sum); for(int k=n; k>1; k-- ){ sum += k ; } System.out.println("k=" + k + ",sum=" + sum); }
1)第1个System.out.println输出语句中变量i未定义;
2)第2个for语句中变量k重复定义,方法参数中定义的是k。
-
对于下面的程序,说法正确的是:BCD
public class Test public static m1(int n , m){ n += m + m2(1.1); System.out.println("n=" + n); } public static int m2(int n){ if(n>0) return 1; else if(n==0) return 0; else if(n<0) return -1; } }
1)方法m1不需要返回值,但必须定义返回值类型为void;
2)方法m1中参数m必须要声明类型;
3)对方法m2的调用错误,不能将double类型的值传递给int类型的形参
4)方法m2中的if语句存在语法错误,最后一个 else if 应改为else。
第七章 一维数组
-
数组的声明
- double[] scores只是声明,存的是数组scores[0]的引用值,是在栈中存储;
new double[10]才是创建数组,为数组开辟的空间,在堆中。 - 声明数组变量时,只是声明一个数组变量并为该变量分配内存,但没有给数组分配内存。
- 创建数组后,数组变量的值只是数组的引用(内存中的首地址),不是包含所有数组元素!
- 数组一旦创建, 其大小不能改变,但是数组变量的值(引用)是可变的,可以引用具有不同大小的数组。
- double[] scores只是声明,存的是数组scores[0]的引用值,是在栈中存储;
-
数组的初始化
-
初始化定义:在声明数组的同时给数组元素赋初值
1.类型标识符[] 数组名= new 类型标识符[] {初值表}
int [] a=new int []{1,2,3};
2.类型标识符[] 数组名= {初值表}
-
注意错误写法
-
用上面第一种方法,不能指定大小
double[] scores = new double[4]{63.0, 82.5, 47, 73};//错!!!!!
-
数组的初始化必须在一行语句中完成。
// 错误的写法1: double[] scores; scores = {63.0, 82.5, 47, 73}; // 错误的写法2: double[] scores = new double[4]; scores = {63.0, 82.5, 47, 73};
// 正确的写法1: double[] scores = {63.0, 82.5, 47, 73}; // 正确的写法2: double[] scores = new double[]{63.0, 82.5, 47, 73}; // 正确的写法3(这不是初始化): double[] scores = new double[4]; double[0]=63.0; double[1]=82.5; double[2]=47; double[3]=73;
-
-
-
foreach循环
-
foreach循环只能遍历不能修改元素的值。
-
foreach循环语法格式
-
-
可变长参数列表
-
具有相同类型的可变长度的参数可以传递给方法, Java JVM 将其作为数组对待
-
方法中可变长参数的声明语法: 方法名(类型名… 参数名 )
-
注意事项:
1.省略号在类型名后(不能是中文省略号)
2.只能给方法指定最多一个可变长参数
3.可变长参数必须是参数列表中最后一个
-
-
Arrays类
-
命令行参数
-
main方法的语法格式: public static void main( String[ ] args )
-
main方法不是一个普通的方法
1.它是所在Java类的执行入口点
2.参数 String[ ] args ,又称为“命令行参数” ,可以在运行时获取输入的参数值。
-
main方法的普通用法。
-
main方法的特殊用法:
运行时从命令行接收字符串参数
-
-
字符串分割转换为字符数组
-
·在java.lang.String类中的String.split()方法,根据给 定的字符来拆分字符串,返回一个字符数组
-
常用语法1:
public String[] split(String regex) //参数regex:包含拆分字符的字符串
-
常用语法2:
public String[] split(String regex, int limit) //参数regex:包含拆分字符的字符串 //参数limit:拆分的份数
-
注意:
*1)若以 . | 和 * - 等为拆分字符,前面必须得加\*
2)若使用多个分隔符,可以用 | 间隔String src = "this is a split test"; String array[] = src.split(" "); //String array[] = src.split("\\."); 以"."分割 //String array[] = src.split("and|or"); 以and或or分割 System.out.println("string: " + src); System.out.println("array:"); for(int i = 0; i < array.length; i++) { System.out.println( i + ": " + array[i] ); }
-
-
数组的复制
使用赋值语句复制数组是错误的
-
不是真正意义上的复制
-
实例解释
int[] a = {65,34,78,81,56,92,56,87,90,77};
int[] b = new int[10];
b = a ;
此时对b数组改变会同时改变a数组,因为是复制了a数组的引用。
实例测试
public class Test {
public static void main(String[] args) {
int[] a = {65, 34, 78, 81, 56, 92, 56, 87, 90, 77};
int[] b = new int[10];
b = a;
b[0] = 15;
System.out.println("b打印");
for (int u : b) {
System.out.print(u+" ");
}
System.out.println("\na打印");
for (int u : a) {
System.out.print(u+" ");
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VOWkk3h9-1641613899772)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112241622463.png)]
真正的数组复制
数组a与数组b需要占用不同的内存空间。
代码测试
public class 数组初始化 {
public static void main(String[] args) {
int a[] = {65,34,78,81,56,92,56,87,90,77};
int [] b = new int[10];
for( int i = 0; i < a.length; i++ )
{
b[i] = a[i] ;
}
b[0] = 15;
System.out.println("b打印");
for (int u : b) {
System.out.print(u+" ");
}
System.out.println("\na打印");
for (int u : a) {
System.out.print(u+" ");
}
}
}
数组复制方法总结
-
使用循环逐元素复制
for( int i = 0; i < list1.length; i++ ) { list2[i] = list1[i] ; }
-
使用 System 类的 arraycopy() 方法
System.arraycopy(sourceArray, src_pos, targetArray, tar_pos, length); // sourceArray: 源数组 // src_pos: 源数组中的起始位置 // targetArray: 目标数组 // tar_pos: 目标数组的起始位置 // length: 要复制的数组元素的数量
题
-
下面对Java语言数组的说明,错误的是:AD
1)声明的数组变量可以指向不同的数组,所以其length属性的值是可变化的,但创建的数组的大小是固定的;
2)数组中每个元素的类型是相同的,可以是基本类型,也可以是引用类型。
-
下面哪些语句是合法的:FG
1)创建数组是用[ ] , 而不是( ), 选项A、E错;
2)声明数组不能指定大小, 选项B、C、D错;
3)选项E没有这种用法。
-
下面方法中,哪几行有语法错误?bcde
1)b行定义数组错误,并且没有初始化,下面是不能直接使用的;
2)c行length()多了圆括号;
3)d行应该为Math.random();
4)e行应该为data[i]。
-
下面两个方法,应输出:0,3
public void method( ){ int[ ] nums = {0, 1, 2, 3}; doM(nums[0], nums); System.out.println(nums[0] + "," + nums[3]); } public void doM( int num, int[ ] b ){ num = b.length; int[ ] a = new int[num]; for(int i=1; i<a.length; i++) a[i] = b[b.length -1-i]; b = a; }
第八章 二维数组
-
二维数组存储空间连续
-
二维数组的初始化
可以使用数组初始化来声明、创建和初始化一个二维数组
-
二维数组的长度
-
二维数组的特殊声明方法
题
-
下列哪些语句是合法的:BC
F:初始化声明与赋值不能分开
-
下面的程序段在执行时的输出值为:2 1 3
int [ ][ ] x = { {0,1} }; int [ ][ ] y = { {1,2}, {3,2,1}, {1} }; int [ ][ ]z= { {1,2}, {1,2,3} }; //将二维数组y第二行的索引赋给z第一行的索引,使z的第一行指向y的第二行 z[1] = y[2]; y[1][1] = x.length; System.out.println(z.length); System.out.println(z[1].length); System.out.println(y[1].length);
第九章 对象和类
-
访问对象
- 用new创建一个对象后,系统为该对象分配内存空间
- 对对象的访问(使用)是通过对象引用变量来实现的
- 为声明一个对象引用变量,使用以下语法: 类名 对象引用变量名 。
- 一步完成对象的声明、创建和引用 类名 对象引用变量 = new 类名( 参数列表 ) ;
-
基本数据类型变量和引用类型变量的区别
-
内存空间
-
赋值
-
如果认为一个对象不再需要了,可以显式地给该对象的引用变量赋 null 值。如 c1 = null。
-
如果该对象没有被其它任何引用变量所引用,Java虚拟机将自动回收它所占的空间。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OJ6lpxwn-1641613899790)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240938794.png)]
-
-
-
静态变量、常量和方法
- 静态变量:用 static 定义的类的属性,由类的所有实例所共享。
- 静态方法:用 static 定义的类的方法,可以由类的所有实例调用。
- 静态常量:用 final static 定义的类的属性,由类的所有实例所共享。
实例:
可见性修饰符
- 可见性修饰符:指明类中的属性和方法是否能从该类之外被访问。
- 在类的内部,对属性和方法的访问是没有任何限制的(除了在静态方法中不能访问实例变量和实例方法外)
缺省修饰符
- 缺省情况下,类、变量或方法都是可以被同一个包内的任意类访问,而对另一个包的所有类不可见
公共修饰符
- 类、数据域或方法对任意包中的任意类可见
- 可见性修饰符public可应用在类或类的成员上
- 不能应用在局部变量上
私有修饰符
- 数据域或方法只能被声明它们所在的类访问,而在类的外部不能访问。
- 可见性修饰符private只能应用在类的成员上
- 不能应用在局部变量上
保护修饰符:
- 同包,同类中可以访问。
- 跨包必须是子类才能访问,子类可通过关键词super访问父类中的protected修饰的方法和属性。
直接通过父类引用访问,不同包下,jvm解析不到是否是子类访问的情况,虽然你是在子类中定义了父类引用。通过super,jvm就知道是子类要访问父类的protected方法。
包
- 若要定义某个类在某个包内,则需在类的首行: package packageName ,声明某些类在某个包中,则这些类在编译时编译成 的.class需放在同一个文件夹中。
- 包名中可以有“ . ” ,表示包的层次结构,相当于文件 系统中的“\” 。
- 使用不同包的类需要导包。
题
-
对于下面的Java类,说法正确的是:
class MyTest{ int i; MyTest(int n){ i = n; } int MyTest( ){ return i; } myTest( ){ } }
1)该类只有一个构造方法:MyTest(int n)
2)该类没有默认的构造方法:MyTest( ),所以此用法错误
3)方法 int MyTest( ) 是一个普通方法,定义正确
4)方法 myTest( ) 不是一个构造方法,则应该要有返回值类型,所以定义错误。
-
对于下面的Java源程序Test.java,在空白(1)处:
public class Test{ public static void main(String[] args){ A a = new A( ); B b = new B(3); Test t = new Test( ); _____________________(1) } int k; } class A{ //属性变量有默认值 int i; public int getI( ){ return i; } } class B{ int j = 1; B(int n){ j = n; } public int getJ( ){ return j; } }
不能为语句:k = 1; ,因为此处不可访问成员变量k
可以为语句:a.getI( ),因为类的属性变量i具有默认的初始值
可以为语句:t.k = 1; ,因为成员变量k对对象t而言是可访问的
-
下面的Java应用程序在执行时的输出是什么 3.0 5.0 2.0
public class Test { public static void main(String[] args){ Test t1 = new Test( ); Test t2 = new Test(2); t1.Test(1); t1.print( ); t1.Test(1.0); t1.print( ); t2.print( ); } double i = 1.0; Test( ){ } Test(int j){ i += 1; } void Test(double j){ i += 2; } void print( ){ System.out.println(i); } }
-
下面的Java应用程序在执行时的输出是什么 1 4 4
public class Test { public static void main(String[] args){ T t1 = new T( ); System.out.println(t1.i+ "," + t1.j); T t2 = new T( ); System.out.println(t1.i+ "," + t1.j); System.out.println(t2.i+ "," + t2.j); } } class T { int i; static int j; T( ){ i += 1; j += 2; } }
1)main方法中第1条语句执行后,t1.i的值为1.0,t1.j的值为2.0
2)main方法中第3条语句执行后,t1.i的值为1.0,t1.j的值为4.0;t2.i的值为1.0,t2.j的值为4.0
3)类T在每一次实例化时,成员变量i先初始化为0,再在构造方法中加1变为1;成员变量j是静态变量,所以只在第1次实例化时初始化为0,以后实例化则不再初始化(已经存在),每次实例化在构造方法中加2
4)对于成员变量i,类T的每个实例化对象均有自己的成员变量i,互不影响;对于成员变量j,类T的所有实例化对象均共享。
-
对于下面的Java应用程序,说法正确的是:AC
public class Test { T t = new T( ); public static void main(String[] args){ m1( ); } public void m1( ){ m2( ); } public static void m2( ){ System.out.println(t.i); System.out.println(T.j); } } class T { int i; static int j; }
第十章 面向对象的思考
类的关系
关联
-
定义:
-
通俗含义:表示事物之间的一种牵连式的固有联系。
-
本质含义:
模型元素之间的一种语义联系, 它是对具有共同的结构特性、行为特性、关系和语义的链接的描述。 -
关联可以分为单向关联,双向关联
-
-
关联的特性:
-
关联名:如果关联关系已经清楚,就无需关联名
-
关联的角色:如果类名与角色名相同,则不标出角色名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8RPrrlu-1641613899798)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240939168.png)]
-
-
关联多重性
一个教师可以开设一到三门课,一门课程有一 到多个教师教授。
-
关联类
通过关联类描述关联的属性,操作,及其它信息。
聚集和组合
聚集的对象生命周期可以不同, 但组合对象则是同存同亡。
-
聚集
聚集(aggregation): 表示类之间一种松散的整体与部分的组成关系,是一种特殊的关联( “has a” 关联 )。没有了也没事
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HzMQaW4F-1641613899800)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240939438.png)] -
组合
组合(composition): 表示类之间一种紧密的整体与部分的 组成关系,也是一种特殊的关联,强调整体与部分不可分割。没有了就gg了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N5pHLvDv-1641613899800)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240939713.png)]
类的泛化
- 泛化(generalization): 表示事物之间的一般与特殊的关系。 也可以称为继承关系。
- 泛化的目的:
- 实现继承 可以使子类共享父类的属性和操作。
- 实现多态可以使子类的实例根据其特殊情况执行特殊的操作,而对外的调用不变。
类的依赖
-
依赖的含义:
- 依赖(dependency): 表示两个元素X、Y,如果X的变化可能会导致Y的变化,则称Y依赖X。
- 依赖关系是单向的
- 依赖关系不仅限于类,用例、包、构件之间都可以存在依赖关系。
-
依赖关系也称使用关系
- 调用: 一个类调用另一个类的方法
- 参数:一个类的方法使用另一个类作为形式参数
- 发送: 消息的发送者与接收者之间的关系
- 实例化: 一个类的方法中创建了另一个的实例。
类的实现
-
两类实现:
-
普通类实现抽象类
-
普通类实现接口
-
Java包装类
-
将Java基本类型值转换为对象来处理
5 → new Integer(5) 5.0 → new Double(5.0)
-
包装类的用途
-
作为和基本数据类型对应的类型存在,方便涉及到对象的操作
某些场合需要对象作为参数,需要将基本类型值转换为对象
-
包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法。
-
-
Java为每个基本类型提供了一个包装类
基本类型与包装类间的转换
-
装箱:将基本数据类型值转换为包装类对象的过程
-
开箱:将包装类对象转换为基本数据类型值的过程
-
实例
-
BigInteger类和BigDecimal类
题
-
下面的语句中,存在错误的是:CE
1)选项C错在参数若是字符串,则必须是纯数字字符串
2)选项E错在包装类均没有无参构造方法
-
对于下面的程序段
int a = Integer.valueOf("10") + 1; int b = Integer.parseInt("10",16 ) - 2;
1)第1条语句正确,Integer.valueOf(“10”)得到的结果是Integer类型,在执行时会自动开箱,再与1相加
2)第2条语句正确,Integer.parseInt(“10”,16 )表示十六进制的10,即十进制的16,减2后得到14
-
对于下面的程序段,
BigInteger sum = 0; for (int i = 1; i <= 100; i++) sum = sum + i; System.out.println(sum);
1)BigInteger类型的常量0应该为 new BigInteger(“0”)
2)若要得到BigInteger类型的变量i的值,应该为new BigInteger("" + i) 或 new BigInteger(i + “”)
3)BigInteger类型的相加运算应该用BigInteger类型的add方法
第十一章 继承和多态
-
重写与重载
-
重写:
-
方法重写条件:①签名相同(方法名、参数列表) ②返回值类型相同/兼容。
-
方法重写时,如果在子类中定义的同名方法的可见性范围比在父类中的小,则会出现编译错误。
-
静态方法可以被子类继承,但是静态方法不能被子类重写
-
父类中定义的静态方法在子类中重新定义,则父类的方法将被隐藏
-
-
重写,重载,重新定义区别:
重写方法签名,返回类型都必须与父类相同;重载只需方法名相同,参数列表不同;重新定义整个方法头(包括修饰符)都相同。
-
-
多态
-
多态意味着父类对象变量可以引用其子类对象,总是可以将子类的实例传递给需要父类类型的地方,形成多态性。
-
多态建立在继承的基础上,子类是父类的特殊化,每个子类实例都是其父类的实例。
-
当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法(为子类中的重写方法或继承的方法),但是不能访问子类独有的属性和方法(例如重载的方法)。
-
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化
-
声明类型与实例类型:
-
声明类型:一个变量必须被声明为某种类型。
Person o
-
实际类型:变量的实际类型,是被变量引用的对象的实际类型。
o = new Student();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrwtvW2J-1641613899812)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240939017.png)]
-
-
动态绑定
一个对象实例既可以使用声明类型的构造方法创建,又可以使用它的子类型的构造方法创建,若父类和子类中有相同的方法(重写的方法),则调用哪个方法由对象实例的实际类型决定。这称为动态绑定(dynamic binding)。
-
匹配方法和绑定方法
-
-
对象转换
-
使用强制类型转换,可以把一种基本类型的变量转换为另 一种基本类型。
int a = 10; double d = (double)a;
-
转换也可以用来把一个类的对象转换为继承层次中的另一 种类型的对象。(自动转型)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UtrUq2Fh-1641613899816)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240939893.png)] -
类型转换实例
-
假设想把对象o的引用赋值给Student类型的变量
自动转型:父类的对象实例不一定是某个子类的实例
子类的对象实例永远是其父类的实例
强制转型:
-
-
从父类到子类的转换
-
若要将对象从父类类型转换为子类类型时必须使用显式类型转换
-
显式类型转换有可能失败
使用instanceof 操作符对其进行判断 -
instanceof 操作符
使用instanceof 操作符可以测试一个对象是否是一个类的实例,得到结果是 boolean 类型。
上图通过instanceof判断obj是Student的一个实例,obj可以显式转换为Student
-
注意:
-
-
-
Object类的equals方法
-
equals() 方法用于比较两个对象。在Object类中的equals方法的实现如下:
public boolean equals(Object obj) { //等价于 this.toString=obj.toString return ( this == obj ); }
等价于 this.toString=obj.toString,可知是比较两个对象的引用是否相等。
-
题
-
下面的说法中正确的是:ACD
1)在子类的构造方法中,父类的构造方法总是在第1条语句,但不一定是父类的无参构造方法,还可以是父类的其它构造方法
2)在子类的构造方法中,可以调用本类的其它构造方法
3)在创建子类实例时,父类的属性总是被初始化的。即实例化子类时,在子类实例的内存空间中,一定包含父类的实例(有父类才有子类)。
-
下面的说法中正确的是:C
-
下面的说法中错误的是:BCD
1)按照继承的特性,任何子类实例均是其父类的实例,但反之不正确
2)因为有继承关系,所以若在一个类中没有定义一个方法,但存在该方法的调用,不会产生编译错误,也不会立即产生运行错误。只有在其所有的父类中均找不到该方法,才会产生运行错误。
-
关于protected和final修饰符,下面的说法中错误的是:
1)类的可见性修饰符只能是缺省的或public
2)final可以修饰类,表示该类是最终的,不可以被继承/扩展
3)final可以修饰成员方法,表示该方法不能被子类重写,但可被子类继承
4)protected可以修饰成员变量或成员方法,表示的可见性范围为在包内或子类中可见
第十二章 异常处理与文本IO
-
捕获运行错误
-
运行错误不是我们想要的,它会引起程序异常终止。
-
需要有某种手段来捕获这个错误,让程序在收到错误信息后能够继续执行。
-
Java使用 try … catch … 结构来捕获异常
-
捕获运行次序
-
-
if与try-catch逻辑比较
-
try-catch异常处理
-
if处理异常
-
-
检测错误与处理错误的分离
-
Java强迫程序员处理必检异常
声明,抛出,捕获异常
-
声明异常
-
每个方法都必须声明它可能会抛出的必检类型的异常(对免检异常可由程序员决定是否声明)
-
使用 throws 选项,声明可能会抛出的异常
-
子类声明可能会抛出的异常类型不能在父类声明的异常类型范围之外
-
-
抛出异常
-
程序检查到一个错误后,可以创建一个异常类型的实例并抛出它,这就称为抛出异常。
-
抛出异常使用 throw 语句
-
throw语句一旦执行,将改变程序的正常执行流程,所在语句块中其后面的语句将不再被执行。
-
throw 语句一般与if 语句结合起来使用
-
参数“信息”可以在异常被捕获后由异常实例对象的getMessage( )方法获取
-
-
捕获异常
- 使用 try … catch … 语句捕获和处理异常
-
如果try块中没有出现异常,会跳过所有的catch子句
-
catch子句的匹配是按先后顺序执行的
-
如果try块中出现异常,会跳过try块中剩余的语句
-
catch中异常的排列顺序是由小的异常类到大的异常类
-
若匹配某一个catch子句,则执行其中的处理代码后跳出
-
若出现异常且catch子句均不匹配,则将异常传给调用者,且不执行catch后的语句
-
如果一个异常没有在当前方法中被捕获,就被传递给该方法的调用者。 这个过程一直反复,直到异常被捕获,或被传递给 main方法。
题
-
对于Java异常类,下面说法正确的是:CD
1)异常类的根类是Throwable类,所以选项A错误。
2)Error类表示JVM出现的错误,所以选项B错误。
3)RuntimeException类表示程序运行时出现的错误,往往表现为程序设计错误,所以选项C正确。
4)Exception类除了可以表示程序出现的错误,还可以表示外部环境引起的错误,所以选项D正确
-
对于必检异常和免检异常,下列说法错误的是:AC
1)必检异常一般表示外部环境引起的异常,所以选项A错误。
2)免检异常表示程序本身出现的问题,或JVM出现的问题,对这些问题编译器不要求程序员在代码中检查,所以选项B正确。
3)必检异常一般表示外部环境引起的异常,Java编译器强制要求程序员在代码中检查这些异常;而免检异常是不需要程序员处理的,所以选项C错误。
3)免检异常往往反映程序本身出现的错误,既然程序有问题,程序员当然要纠正这些错误代码,所以选项D正确。
-
对于下面方法的定义,说法正确的是:BCD
1)该方法通过throws A, B的声明,告知它的调用者本方法有可能会抛出A和B两种异常。当然,如果该方法中出现其它异常而在该方法中没有处理的话,编译器也会将该异常抛给调用者。所以选项B正确。
2)throw new C("");语句表示抛出异常,既然在代码中显式地抛出异常,则一般会在方法的头部声明这种异常。所以选项C正确。(注:C若为其它免检类型的异常也不会报错)
3)一旦抛出C类异常,则后面的语句不被执行。所以选项D正确。
-
假设下面的try-catch块中的statement2发生一个异常,下面说法正确的是:
try { statement1; statement2; statement3; } catch (Exception1 ex1) { statement4; } catch (Exception2 ex2) { statement5; } finally { statement6; } statement7;
1)若异常类型是Exception1,则statement4、statement6和statement7会被执行,所以选项A错误。
2)若异常类型是Exception2,则statement4不会被执行,而statement5、statement6和statement7会被执行,所以选项B正确。
3)若异常类型是Exception3,则异常没有被捕获,则只会执行statement6,所以选项C正确。
4)在statement2处不管发生什么类型的异常,statement3一定不会被执行,而statement6一定会被执行,所以选项D正确。
第十三章 抽象类和接口
抽象类介绍
- 用abstract修饰类,限制为只能当类型,不能实例化;用abstract修饰方法,限制为只能由子类来具体实现
- 使用抽象类的目的:
- 抽象类避免了该类被实例化,功能类似于“模板”
- 增强了面向抽象编程思想,便于写出通用的代码(面 向对象设计的核心之一)
- 只有抽象类才能容纳抽象方法(定义抽象类的目的之一)
抽象类的声明与使用
声明抽象方法:
注意事项:
- 抽象类中,可以包含抽象方法,也可以不包含抽象方法。
- 若包含抽象方法,则该类必须声明为抽象类。
- 抽象类中也可以定义非抽象方法。
- 抽象类的非抽象子类必须给出每个抽象方法的具体实现,若有抽象方法在子类中没有被实现,该子类必须也必须声明为抽象。
- 子类可以声明为抽象的,即使它的父类是具体的
接口
-
为什么需要接口
- Java语言的继承只能是单一结构 一个类最多只有一个父类,接口(interface)可以被用来实现多继承结构。
- 接口指明多个类表现出的共同行为模式或特性。
- 接口提供了方法声明与实现相分离的机制,每个实现接口的类可以根据各自要求,给出抽象方法的具体实现。
-
接口的结构
-
接口只包含常量和抽象方法
-
与抽象类相似,不能使用new操作符创建一个接口的实例。
-
成员变量只能是公有的、静态的常量
-
接口中定义的方法都是公有的、抽象的
-
非抽象类必须要实现接口中所有的抽象方法
-
在类中实现的抽象方法的可见性范围不能缩小
-
在接口中,所有的数据域都是public final static的,所有的 方法都是public abstract的。 由于这个原因,这些修饰符可以忽略,如下所示:
-
Java 8 新方法
-
Comparable接口
-
同类对象间经常有“比较”的操作 ,要想同类对象可比较,该类对象需要具有“可比较的”特性 (comparable)
-
Comparable接口定义了compareTo方法,实现了该接口的类型的对象均具有可比较性.
package java.lang; public interface Comparable <E> { public int compareto( E o ); }
-
如果当前对象大于、等于或小于给定对象o时,分别返回正整 数、0 或 负整数。
-
Comparable接口支持泛型。在实现该接口时,可以将类型参数E替换为一个具体的引用类型。
-
不同类的具体实现方式会不同
- 数值类和日期类的实现是大于和小于分别返回1和-1
- 字符串类的实现是返回真实的大小比较值。
public class TestComparable { public static void main(String[] args){ System.out.println( new Integer(3).compareTo(new Integer(5))); System.out.println( "ABC".compareTo("ABE") ); Calendar date1 = new GregorianCalendar(2017,11,1); Calendar date2 = new GregorianCalendar(2016,11,1); System.out.println( date1.compareTo(date2) ); } }
结果:-1,-2,1。
Cloneable接口
-
为什么要克隆接口?
-
基本类型的拷贝
int x = 10 , y = 5; y = x ;
-
引用类型的拷贝
Circle c1 = new Circle( 5 ); Circle c2 = new Circle( 3 ); c2 = c1 ;
不是真正的拷贝!
-
-
Cloneable接口的使用
- Cloneable接口给出了一个可克隆的对象,如果一个类实现了Cloneable接口,则其实例对象就 可以调用Object类中定义的clone方法进行克隆
- Cloneable接口是一个标记接口,即内容为空。实现该接口仅仅表示某个类具有某种特性。
-
实现Cloneable接口的自定义类
-
若要使自定义类的对象实例具有clone方法,则必须 实现Cloneable接口,并重写clone方法,clone方法是在Objec类中定义的,语法格式为:
protected native Object clone() throws CloneNotSupportedException;
原可见性修饰符为protected,重写clone方法时一般为public (可见性范围不能缩小)
-
-
浅复制 VS 深复制
-
浅复制:复制时将原始对象的基本类型数据域的值复制给目标对象(包括String类型数据域),而对引用类型的数据域, 仅复制引用。
-
深复制:复制时将原始对象的基本类型数据域的值复制给目标对象(包括String类型数据域),并且对所有引用类型的数据域,创建全新的对象,再将其数据域复制过去。
-
实例:
-
接口和抽象类
-
接口可以继承接口而不是类(接口可以多重继承)
-
使用接口还是抽象类呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eZXAZmL2-1641613899854)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112240940131.png)]
-
接口和抽象类的异同
-
相同点
都包含抽象方法 ,都不能被实例化
-
不同点
- 类只能单一继承自某个父类;接口可以继承多个接口
- 接口一般只包含常量和抽象方法(Java 8 可以有默认的实现方法); 抽象类中可以包含成员变量、构造方法、抽象方法和普通方法。
- 接口中成员的可见性只能是public;抽象类中成员的可见性可以有多种可见性修饰符。
- 在接口里,数据必须是常量;抽象类可以有多种类型的数据
- 接口没有共同的根;所有的类共享同一个根:Object类。
-
题
-
对于Java抽象类,下列说法错误的是:BC
1)抽象类不能实例化,但抽象类同样需要构造方法。抽象类中可以含有数据域,构造方法可以用来初始化这些数据域。
2)抽象类不能实例化,但抽象类可以有实例成员变量。在子类可以用super语句调用父类抽象类的构造方法,在子类实例化内存空间中初始化父类抽象类的实例成员变量。
3)抽象类用于扩展,不能实例化;抽象类中可以有抽象方法和非抽象方法,也可以没有抽象方法。所以选项AD正确,选项BC错误。
-
对于Java抽象方法,下列说法正确的是:AB
1)抽象方法不能存在于非抽象类中,所以如果一个类包含抽象方法,则该类一定只能声明为抽象类。所以选项A正确。
2)抽象方法只有方法的声明,没有方法体。所以选项B正确。
3)抽象方法不能声明为静态的,所以不可以用static修饰符。所以选项C错误。
4)类的构造方法不能被子类继承,不能被子类重写,所以抽象类的构造方法必须是具体的方法。所以选项D错误。
-
对于Java接口,说法正确的是:ABCD