一.面型对象(上)
第一部分:习题
1.JDK,JRE,JVM三者之间的关系,以及JDK、JRE包含的主要结构有哪些?
三者是包含关系
JDK=JRE+JAVA开发工具(javac.exe java.exe javadoc.exe)
JRE=JVM+JAVA核心类库
2.为什么要配置path环境变量?如何配置?
为了在任何一个文件路径下都可以执行JAVA的开发工具
JAVA_HOME=bin的上一层目录
path=%JAVA_HOME%\bin
3.常用的几个命令行操作都有哪些?(至少4个)
cd
md:创建文件目录
rd:删除文件目录
del:删除文件
cd .. :退回到上一级目录
cd /:回到根目录
4.创建如下的类,使得运行的话可以输出:
创建java文件:ChairMan.java
public class ChairMan{
public static void main(String[] args){
System.out.println("");
}
}
5.编译和运行上述代码的指令
编译:javac ChairMan.java
运行:java ChairMan
1.标识符的命名规则有哪些? 不遵守,编译不通过。
26个英文单词,$,数字,下划线
不能以数字开头
2. 标识符的命名规范有哪些?不遵守,编译运行都可以通过
包名:xxxyyyzzz
类名、接口名:XxxYyyZzz
变量名、方法名:xxxYyyZzz
常量名:XXX_YYY_ZZZ
3. Java变量按照数据类型怎么划分?并指出Java的基本数据类型有哪8种,并指出各自占用的内存空间大小
byte short int long
char
float double
boolean
引用数据类型:类、接口、数组
4. 说明基本数据类型变量之间自动类型提升的运算规则。
byte、short、char -》int -》long -》float -》double
5. 说明基本数据类型变量之间强制类型转换的使用规则和强转可能出现的问题。
容量大 -》容量小,4中右边的往左边转换
使用强转符:()
精度损失。
1.“&”和“&&”的异同
逻辑与- 短路与
2.程序输出
class OperatorTest { public static void main(String[] args) { boolean x = true; boolean y = false; short z = 40; if ((z++ == 40) && (y = true)) { z++; } if ((x = false) || (++z == 43)) { z++; } System.out.println("z = " + z); } } |
结果为:44
3.定义三个int型变量并赋值,使用三元运算符或者if-else获取这三个数中的较大数的实现
4.编写程序,声明2个double型变量并赋值。判断第一个数大于10.0,且第2个数小于20.0,打印两数之和。否则,打印两数的乘积。
5. 交换两个变量值的代码的实现
1.switch后面使用的表达式可以是哪些数据类型的。
byte short char int 枚举类型变量 string类型
枚举类型定义的一般形式为: enum 枚举名{ 枚举值表 };在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。
2. 使用switch语句改写下列if语句:
int a = 3;
int x = 100;
if(a==1)
x+=5;
else if(a==2)
x+=10;
else if(a==3)
x+=16;
else
x+=34;
int a = 3;
int x = 100;
swith(a){
case 1:
x+=5;
break;
case 2:
x+=10;
break;
case 3:
x+=16;
break;
default:
x+=34;
//break;
}
3. 谈谈你对三元运算符、if-else和switch-case结构使用场景的理解
三元运算符用于有运算的二选一情况中。
4. 如何从控制台获取String和int型的变量,并输出?使用代码实现
import java.util.Scanner;
Scanner scan =new Scanner(System.in);
String info =scan.next();
int id=scan.nextInt();
5. 使用for循环遍历100以内的奇数,并计算所有的奇数的和并输出。
1. 循环结构是如何最后退出循环的,有哪些不同的情况请说明。
① 循环条件返回false
② 在循环体内,一旦执行到break,跳出循环
continue;return
2.指出如下程序输出的结果:
label: for (int i = 1; i <= 4; i++) { for (int j = 1; j <= 10; j++) { if (j % 4 == 0) { continue label; } System.out.print(j); } System.out.println(); } |
正确结果:123123123123
3.一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3。编程 找出1000以内的所有完数。(因子:除去这个数本身的其它约数)
4. 说明break和continue使用上的相同点和不同点
break:switch-case 和 循环结构(结束当前循环),其后不可以声明执行语句
continue: 循环结构(结束当次循环),其后不可以声明执行语句
5. 从控制台输出如下结构:
******
*****
****
***
**
*
外层循环控制行数
内层循环控制列数
for(int i=1;i<6;i++){
for(int j=1;j<=7-i;j++){
Sout("*");
}
soutln;
}
1.写出一维数组初始化的两种方式
int[] arr = new int[5];//动态初始化
String[] arr1 = new String[]{"Tom","Jerry","Jim"};//静态初始化
数组一旦初始化,其长度就是确定的。arr.length
数组长度一旦确定,就不可修改。
2.写出二维数组初始化的两种方式
int[][] arr = new int[4][3];//动态初始化1
int[][] arr1 = new int[4][];//动态初始化2
int[][] arr2 = new int[][]{{1,2,3},{4,5,6},{7,8}};//静态初始化
3.如何遍历如下的二维数组
int[] arr = new int[][]{{1,2,3},{4,5},{6,7,8}};
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr[i].length;j++){
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
4.不同类型的一维数组元素的默认初始化值各是多少
整型 : 0
浮点型:0.0
char:0
boolean :false
引用类型:null
5.一维数组的内存解析:
String[] strs = new String[5];
strs[2] = “Tom”;
strs = new String[3];
1.使用冒泡排序,实现如下的数组从小到大排序。
int[] arr = new int[]{34,5,22,-98,6,-76,0,-3};
for(int i = 0;i < arr.length - 1;i++){
for(int j = 0;j < arr.length - 1 - i;j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
快排时间复杂度:O(nlogn)
冒泡时间复杂度:O(n^2)
堆排序、归并排序
2.如何反转上面的数组。请代码实现
略
3. 复制上述数组,得到一个新的数组
vs 赋值操作
array2 = array1;
int[] arr1 = new int[arr.length];
for(;;)...
4. 使用线性查找,从上述数组中查找22是否存在。存在,返回所在位置的索引。不存在,输出提示信息。
int dest = 22;
boolean isFlag = true;
for(int i = 0;i < arr.length;i++){
if(dest == arr[i]){
sysout(i);
isFlag = false;
break;
}
}
if(isFlag){
sysout("未找到");
}
或
int dest = 22;
for(int i = 0;i < arr.length;i++){
if(dest == arr[i]){
sysout(i);
break;
}
}
if(i == arr.length){
sysout("未找到");
}
5. 数组中常见的异常有哪些?请举例说明
ArrayIndexOutOfBoundsException:数组角标越界异常:
合理范围:[0,arr.length -1]
越界:arr[-1],arr[arr.length]
NullPointerException:空指针异常
int[] arr = null;
arr[0];
1.面向对象思想编程内容的三条主线分别是什么
① 类及类的成员:属性、方法、构造器;代码块、内部类
② 面向对象的三大特征:封装、继承、多态
③ 其它关键字:this,super,abstract,interface,static,final,package,import
面向对象的编程思想?
(类、对象;面向对象的三大特征;。。。)
2.谈谈你对面向对象中类和对象的理解,并指出二者的关系?
类:抽象的、概念上的内容
对象:实实在在存在的一个个体,内存中存在的。
对象是由类派生出来的,new出来的。
例如,后台提供了一个Scanner类,而实际操作过程中需要创建一个Scanner对象
3. 面向对象思想的体现一:类和对象的创建和执行操作有哪三步?
① 创建类
② 类的实例化,/创建类的对象,new对象
③ 调用对象的结构:”对象.属性” “对象.方法”
4. 画出如下代码在执行时的内存分配情况
class Car{
String color = "red";
int num = 4;
void show(){
int a = 10;
System.out.println("color="+color+",num="+num);
}
}
class CarTest {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car();
c1.color = "blue";
c1.show();
c2.show();
} }
5. 类的方法内是否可以定义变量?是否可以调用属性?是否可以定义方法?是否可以调用方法?
是;是;否;是
1. 什么是方法的重载?
“两同一不同”:同一个类、相同方法名;参数列表不同。
如何调用确定的方法:方法名à参数列表
2. 说明Java方法中的参数传递机制的具体体现?
基本数据类型:数据值
引用数据类型:地址值 (含变量的数据类型)
Person p1 = new Person(); eat();age
User u1 = p1;//编译错误 (逆向思维、反证法)
u1.eat() u1.age
3. 成员变量和局部变量在声明的位置上、是否有默认初始化值上、是否能有权限修饰符修饰上、内存分配的位置上有何不同?
4. 谈谈return关键字的使用
① 结束方法 ② 针对于有返回值的方法,return + 返回数据
5. 提供如下代码的内存解析
1. 内存结构:栈(局部变量)、堆(new出来的结构:对象(非static成员变量)、数组)
2. 变量:成员变量 vs 局部变量(方法内、方法形参、构造器内、构造器形参、代码块内)
第二部分:知识点
重载问题:
可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,二者不能共存
在一个方法的形参中,可变个数形参只能声明在所有形参的末尾,因此在一个方法中最多只能声明一个可变形参
变量的赋值:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
方法形参的传递机制:值传递
1.形参:方法定义时,声明的小括号内的参数
2.实参:方法调用时,实际传递给形参的数据
3.值传递机制: 如果参数是基本数据类型,此时实参赋值给形参的是实参真实存储的数据值
方法的重载与重写的区别:
从编译和运行的角度看:重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名的方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。他们的调用地址在编译器就绑定了。JAVA的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“”静态绑定“;
而对于多态而言,只有等到方法调用的那一刻,编译器才会确定所要调用的具体方法,称为“晚绑定”或“动态绑定”。
①两者的概念
②重载和重写的具体规则
③重载不表现为多态性;重写表现为多态性
throws\throw:
String\StringBuffer\StringBuilder:
Collection\Collections:
final\finally\finalize:
>final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载
>finally—再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)
>finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的
"=="和equals()的区别:
==:运算符
1、可以使用在基本数据类型和引用数据类型变量中
2、如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定类型要相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
equals():方法
1、是一个方法,不是运算符
2、不能使用在基本数据类型中,只适用于引用数据类型
3、Object类中equals()的定义和“==”的作用相同
4、像String、Data、File、包装类等都重写了Object类中equals()方法,重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同。
5、自定义类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同。那么,我们就需要对Object类中equals()方法进行重写。重写的原则:比较两个对象的实体内容是否相同。
基本数据类型就用==,涉及到引用数据类型,就用equals()
面向对象的特征一:封装与隐藏
一、问题的引入
当我们创建一个类以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。但除此之外,没有其他制约条件。
但是在实际问题中,我们往往要给属性赋值加入额外的限制条件,这个条件就不能在属性的声明时体现,我们只能通过方法进行限制条件的添加。(比如:set)。同时,我们需要避免用户再使用“对象.属性”的方式进行赋值。则需要将属性声明为私有的(private)。-->此时,针对于属性就体现了封装性。
二、封装性的体现
我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXXX)和设置(setXXX)此属性的值。
三、类的构造器 constructor
作用:创建对象;初始化对象的属性
说明:1、如果没有显式的定义累的构造器的话,系统默认提供一个空参的构造器
2、定义构造器的格式:权限修饰符 类名(形参列表){ }
3、一个类中定义的多个构造器,彼此构成重载
4、一旦我们显式的定义了累的构造器之后,系统就不再提供默认的空参构造器
5、一个类中,至少有一个构造器
this关键字:
1、this可以用来修饰、调用:属性,方法,构造器
2、this修饰属性和方法“this理解为:当前对象
2.1.在类的方法中,我们可以使用:this.属性 或者this.方法,调用当前对象属性或者方法。但是通常情况下,我们选择省略this.。特殊情况下,如果方法的 形参和类的属性同名时,我们必须显式的使用this.变量的方法,表明此变量是属性,而非形参。
2.2.在构造器的方法中,我们可以使用:this.属性 或者this.方法,调用当前对象属性或者方法。但是通常情况下,我们选择省略this.。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用this.变量的方法,表明此变量是属性,而非形参。
怎么在一个构造器中调用另外一个构造器?
this调用构造器:
1、我们在类的构造器中,可以显式的使用”this(形参列表)“的方式,调用本类中指定的其他构造器。
2、构造器中不能通过”this(形参列表)“方式调用自己
3、如果一个类中有n个构造器,则最多有n-1构造器中使用了”this(形参列表)“
4、”this(形参列表)“这种方式,必须用在构造器的首行
5、构造器内部,最多只能声明一个”this(形参列表)“,用来调用其他的构造器
package关键字:
1、为了更好的实现项目中类的管理,提供包的概念
2、使用package声明类或接口所属的包,声明在源文件的首行
3、包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、”见名知意“
4、每”.“一次,就代表一层文件目录
补充:同一个包下,不能命名同名的接口、类;不同的包下,可以命名同名的接口、类
import关键字:
1、导入,在源文件中显式的使用import结构导入指定包下的类、接口
2、生命在package和类的声明之间,多个结构并列写入
3、可以使用”XXX.*“的方式,表示可以导入XXX包下的所有结构
4、如果使用的类或接口是java.lang包下定义的,则可以省略import结构
5、如果使用的类或接口是本包下定义的,则也可以省略import结构
6、如果在源文件中使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式进行显式:例如:com.atguigu.exer3.Account
7、如果使用"XXX.*"的方式表明可以调用XXX包下的所有结构。但是如果使用的是XXX子包下的结构,则仍需要显式的导入
8、import static:导入指定类或接口中的静态结构:属性或方法(用的比较少)
super关键字:父类的
调用属性和方法:
1、我们可以在子类的方法或构造器中,通过“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略“super”
2、特殊情况:当子类和父类中定义了同名的属性时,我们想要在子类中调用父类中声明的属性,则必须显式的使用“super.属性”的方式,表明调用的是父类中声明的属性
3、特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用“super.属性”的方式,表明调用的是父类中被重写的方法。
调用构造器:
1、我们可以在子类的构造器中显式的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
2、“super(形参列表)”的使用,必须声明在子类构造器中的首行
3、我们在类的构造器中,针对“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现
4、当我们在构造器的首行没有显式的声明“this(形参列表)”或“super(形参列表)”,则默认调用的是父类中空参的构造器“super(形参列表)”
5、在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器
子类对象实例化的全过程:
1、从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
2、从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或者间接的调用其父类的构造器,进而调用父类的构造器,知道调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,虽然调用了父类的构造器,但是自始至终只创建了一个对象,即为new的子类对象
重写的规则:
方法名、形参列表相同
权限修饰符不能小于父类的
返回值
抛出的异常
约定俗成:子类中的叫重写的方法,父类中的叫被重写的方法
①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
③返回值类型:
父类被重写的方法返回值类型是void,则子类重写的方法的返回值类型只能是void
父类被重写的方法返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A的子类
父类被重写的方法返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(double)
④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常
多态性:
1、理解多态性:可以理解为一个事物的多种形态
2、何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
3、多态的使用:虚拟方法调用
有了对象的多态性以后,在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
总结:编译看左边,运行看右边
4、多态性的使用前提:①类的继承关系 ②要有方法的重写
5、对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)
什么是虚拟方法调用:调用方法时,编译时候看的是左边的父类,实际运行时看的是右边的子类
instanceof关键字
Person p2=new Man();
不能调用子类所特有的方法、属性;编译时,P2是Person类型
问题:有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
如何调用子类特有的属性和方法呢?
向下转型:使用强制类型转换符
Man m1=(Man)p2;
为了在使用向下转型时出现classCastException异常,在向下转型之前,先进行instanceof的判断。一旦返回true,就进行向下转型,如果返回false,不进行向下转型。
如果 a instanceof A返回true ,则a instanceof B也返回true,其中,类B是类A的父类
a instanceof A:判断对象a是否是类A的实例,如果是-》true,如果不是-》false.
toString()方法:
1、当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
2、Object类中toString()的定义:
public String toString(){
return getclass().getName()+"@"+Interger.toHexString(hashCode());
}
3、像String、Data、File、包装类等都重写了Object类中的toString()方法,使得调用toString()方法时,返回“实体内容”信息
4、自定义也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”
包装类的使用:(Wrapper)
Java中的Junit单元测试
步骤:
1、选中当前工程-右键选择:build path -add libraries-JUnit 4-下一步
2、创建JAVA类,进行单元测试
此时的JAVA类要求:①此类是public的 ②此类提供公共的无参构造器
3、此类中声明单元测试方法
此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4、此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
5、声明好单元测试方法以后,就可以在方法体内测试相关的代码
6、写完代码以后,左键双击单元测试方法名,右键:run as -JUnit Test
基本数据类型、包装类与String三者如何转换:
基本--》包装:
自动装箱、自动拆箱
Integer i=10;
基本数据类型、包装类--》String:String:valueOf(Xxx xx)
String--》基本数据类型、包装类:parseXxx(String s)
Static关键字
可以用来修饰:属性(实例变量和静态变量)、方法、代码块、内部类
1、使用static修饰属性: 静态变量(或类变量)
1.1属性:按是否使用static修饰,又分为:静态属性和非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
1.2static修饰属性的其他说明:
①静态变量随着类的加载而加载,可以通过"类.静态变量"的方式进行调用
②静态变量的加载要早于对象的创建
③由于类只会加载一次,则静态变量的内存中也只会存在一份,存在方法区的静态域中
④ 类变量 实例变量
类 yes no
对象 yes yes
1.3静态属性举例:System.out; Math.PI
2、使用static修饰方法:静态方法
①随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
②
静态方法 非静态方法
类 yes no
对象 yes yes
③ 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用静态的方法或属性,也可以调用调用非静态的方法或属性
3、static注意点:
在静态的方法内,不能使用this关键字、super关键字
4、在开发中如何确定一个属性是否声明为static?属性是可以被多个对象所共享的,不会随着对象的不同而不同
在开发中如何确定一个方法是否声明为static?1.操作静态属性的方法,通常设置为static的;2.工具类中的方法,习惯上声明为static,比如:Math、Arrays、Collections
单例设计模式Singleton:
设计模式:是在大量的实践中总结和理论化之后的代码结构、编程风格、以及解决问题的思考方式。
1、所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
2、如何实现?
饿汉式 vs懒汉式
3、区分饿汉式 和懒汉式
饿汉式:坏处:在一开始的时候,就创建好了对象,导致对象加载时间过长,生命周期长、
好处:饿汉式是线程安全的
懒汉式:好处:延迟对象的创建
坏处:目前的写法,线程不安全----》到多线程的时候可以修改
4、使用场景:
单例模式的优点:由于单例模式只生成一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时,入读取配置、生产其他依赖对象时,则可以通过在启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
类的成员之四:代码块(或初始化块)
1、代码块的作用:用来初始化类、对象
2、代码块如果有修饰的话,只能使用static
3、分类:静态代码块 VS 非静态代码块
4、静态代码块:
内部可以有输出语句;
随着类的加载而执行(无法调用,所以自动执行了),而且只执行一次;
作用:初始化类的信息;
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行;
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
静态代码块比非静态代码块先执行
5、非静态代码块:
内部可以有输出语句;
随着对象的创建而执行;每次创建一个对象,非静态代码块都执行一次;
作用:可以在创建对象时,对对象的属性等进行初始化。
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行;
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
对属性可以赋值的位置:
①默认初始化
②显式初始化、⑤在代码块中赋值(看谁先写)
③构造器中初始化
④有了对象以后,可以通过“对象.属性”或“对象.方法”的方式进行赋值
执行先后顺序:①-②/⑤-③-④
总结运行顺序:由父及子,静态先行
final关键字:
1、final可以用来修饰类、方法、变量
2、final用来修饰一个类:final修饰的类不能被继承/不能有子类,例如String类、 System类、StringBuffer类
3、final用来修饰方法:表明此方法不可以被重写,例如 Object类中gatClass();
4、final用来修饰变量:此时的“变量”就成为一个常量,不能被修改
4.1、final修饰属性:可以考虑赋值的位置有:②显式初始化、⑤代码块中赋值、③构造器中初始化、
4.2、final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法是给这个常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:全局常量 用来修饰方法:
abstract关键字:
abstract可以用来修饰的结构:类、方法
abstract修饰类:抽象类
>此类不能实例化,不能造对象了
>抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
>开发中,都会提供抽象类的子类,让子类对象实例化
abstract修饰方法:抽象方法
public abstract void eat();
>抽象方法只有方法的声明,没有方法体
>包含抽象方法的类一定是一个抽象类,因为抽象方法不能被对象调用,抽象类没有对象,子类可以重写该方法。反之,抽象类中可以没有抽象方法。
>若子类重写了父类中的所有的抽象方法后,此类方可实例化
若子类没有重写了父类中的所有的抽象方法,此子类也是一个抽象类,需要使用abstract
abstract使用上的注意点:
1、abstract不能用来修饰:属性、构造器等结构
2、abstract不能用来修饰私有方法、静态方法(静态方法不能被重写)、final的方法、final的类
问题1、为什么抽象类不可以使用final关键字声明?
抽象类一般都需要子类去继承,而final关键字修饰的类不可以被继承
问题2、抽象类中可以定义构造器吗?
可以,虽然不能造对象,但是可以定义构造器,因为子类还需要调用构造器
问题3、是否可以这样理解:抽象类就是比普通类多定义了抽象方法,除了不能直接进行类的实例化操作之外,没有任何的不同?
可以,加上abstract就是明确说明不要去new这个类的对象了,也可能没有抽象方法,但是通常都有抽象方法。
>抽象举例一:
>抽象举例二:
>抽象举例三:IO流中涉及到的抽象类:InputStream、OutputStream、Reader、Writer
在其内部定义了抽象的read()、write()方法。
设计模式:模板方法:
1、解决的问题:在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变。易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
2、举例:
接口:interface
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,JAVA不支持多重继承。有了接口就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3、手机、数码相机、移动硬盘等都支持USB链接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个“是不是”的关系,而接口实现则是“能不能”的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
接口的使用:
1、接口使用interface来定义
2、JAVA中,接口和类是并列的两个结构
3、如何让定义接口,定义接口中的成员:
3.1 JDK7以前:只能定义全局常量和抽象方法
>全局常量:public static final 的,但是书写时,可以省略不写,但也是默认有的
>抽象方法:public abstract的,但是书写时,可以省略不写,但也是默认有的
3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
>接口中定义的静态方法,只能通过接口来调用,相当于工具类。
>通过实现类的对象,可以调用接口中定义的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法。
>如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法--类优先原则。
>如果实现类实现了多个接口,在多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错==》接口冲突,这就需要我们必须在实现类中重写此参数。
>如何在子类(或实现类)中调用父类、接口中被重写的默认方法?
super.method3();
compareA.super.method3();
compareB.super.method3();
4、接口中不能定义构造器,意味着接口不能实例化
5、JAVA开发中,接口都通过让类去实现(implement)的方式来使用。
--如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
--如果实现类没有覆盖了接口中的所有抽象方法,则此实现类仍为一个抽象类
6、JAVA类可以实现多个接口 --->弥补了JAVA单继承的局限性
格式:class AA extends BB implements CC,DD,EE
7、接口与接口之间可以继承,并且可以多继承
8、接口的具体使用,体现多态性
因为不能实例化,如果一个方法的形参写成了抽象类或者接口,用的话就要实现它的子类或者实现类的方法
9、接口,实际上可以看作是一种规范
面向接口编程:我们在应用程序中,调用的结构都是JDBC中定义的接口,不会出现具体的某一个数据库厂商的API
内部类:
当一个事物的内部,还有一个部分需要一个顽症的结构进行描述,而这个内部的顽症的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
1、在JAVA中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者成为外部类。
2、内部类的分类:成员内部类(静态、非静态) VS 局部内部类(方法内、代码块内、构造器内)
3、成员内部类:
>一方面,作为外部类的成员
>调用外部类的结构
>可以被static修饰
>可以被四种不同的修饰符
>另一方面,作为一个类
>类内可以定义属性、方法、构造器
>可以被final修饰,表示此类不能被继承、言外之意,不使用final,就可以被继承
>可以被abstract修饰
4、关注如下三个问题:
4.1如何实例化成员内部类的对象
4.2如何在成员内部类中区分调用外部类的结构
4.3开发中局部内部类的使用
注意点:在局部内部类的方法中,如果调用局部内部类所声明的方法中的局部变量的话,要求此局部变量声明为final的。
总结:
成员内部类和局部内部类 ,在编译以后,都会生成字节码文件
格式:成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class
面试题:抽象类和接口又哪些共同点和区别?
相同点:不能实例化;都可以包含抽象方法
不同点:抽象类有构造器;接口(JDK7、JDK8)没有构造器
1、 定义上:两者表达的概念不一样。抽象类是一类事物的高度聚合,那么对于继承抽象类的子类来说,对于抽象类来说,属于“是”的关系;而接口是定义行为规范,因此对于实现接口的子类来说,相对于接口来说,是“行为需要按照接口来完成”。这些听起来有些虚,举个例子。例如,狗是对于所有狗类动物的统称,京哈是狗,牧羊犬是狗,那么狗的一般特性,都会在京哈,牧羊犬中找到,那么狗相对于京哈和牧羊犬来说,就属于这类事物的抽象类型;而对于“叫”这个动作来说,狗可以叫,鸟也可以叫。很明显,前者相当于所说的是抽象类,而后者指的就是接口。
2、方法的实现:抽象类在定义类型方法的时候,可以给出方法的实现部分,也可以不给出;而对于接口来说,其中所定义的方法都不能给出实现部分。继承类对于两者所涉及方法的实现是不同的。继承类对于抽象类所定义的抽象方法,可以不用重写,也就是说,可以延用抽象类的方法;而对于接口类所定义的方法或者属性来说,在继承类中必须要给出相应的方法和属性实现。在抽象类中,新增一个方法的话,继承类中可以不用作任何处理;而对于接口来说,则需要修改继承类,提供新定义的方法。Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以(就是interface中只能定义方法,而不能有方法的实现,而在abstract class中则可以既有方法的具体实现,又有没有具体实现的抽象方法),这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个 新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点。
3、单继承&多继承:一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。(使用抽象类,那么继承这个抽象类的子类类型就比较单一,因为子类只能单继承抽象类;而子类能够同时实现多个接口,因为类型就比较多。接口和抽象类都可以定义对象,但是只能用他们的具体实现类来进行实例化。)
此处参考链接:https://blog.csdn.net/xw13106209/article/details/6923556
面试题:如何创建静态成员内部类和非静态成员内部类的对象?
接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?
答案是: 接口可以继承接口。抽象类可以实现(implements)接口,
抽象类可继承实体类,但实体类必须不能是如下两种情况之一:
1,final修饰符修饰的类是不能的
2,如果此实体类有且仅有私有的构造函数也是不能的。
面试题:写一个Singleton出来
package atguigu.java7;
public class SingletonTest1 {
public static void main(String[] args) {
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance();
System.out.println(bank1==bank2);
}
}
//饿汉式
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此对象也必须是静态的
private static Bank instance=new Bank();
//3.提供公共的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
package atguigu.java7;
public class SingletonTest2 {
public static void main(String[] args) {
Order order1=Order.getInstance();
Order order2=Order.getInstance();
System.out.println(order1==order2);
}
}
//懒汉式:
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类对象,没有初始化
//4.要求此对象也必须是静态的
private static Order instance=null;
//3.声明Public,static的返回当前类对象的方法
public static Order getInstance(){
if(instance==null) {
instance =new Order();
}
return instance;
}
}
异常:
Error:JAVA虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况,比如:StackOverflowError(栈溢出)和OOM(堆溢出)
Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性代码进行处理。
>空指针访问
>试图读取不存在的文件
>网络链接中断
>数组角标越界
对于这些错误,一般有两种解决错误:一是遇到错误就终止程序的运行。另一种方法是由程序员在编写程序的时候,就考虑到错误的检测、错误消息的提示,以及错误的处理。
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等
分类:编译时异常和运行时异常
面试题:常见的异常有哪些?举例说明:
运行时异常:
(输入时不输入int类型的,就会报错)
编译时异常:
异常处理机制一:
在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行X/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的家肠、臃肿,可读性差。因此采用异常处理机制。
JAVA异常处理:
JAVA采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
异常处理:抓抛模型:
过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码生成一个对应异常类的对象,并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:>系统自动生成的异常
>手动生成一个异常对象,并抛出(throw)
过程二:“抓”:可以理解为异常的处理方式:①try-catch-finally②throws
二、try-catch-finally的使用
说明:
1、finally是可选的。
2、使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
3、一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常处理。一旦处理完成,就跳出当前的try-catch结构(在没有鞋finally的情况)。继续执行气候的代码。
4、catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果没有子父类关系,则一定要求子类声明在父类上边,则报错
5、常用的异常对象处理的方式:①String getMessage() ②printStackTrace()
6、try结构中声明的变量,在出了try结构以后,就不能再被调用
7、try-catch-finally可以嵌套
体会一:使用try-catch-finally处理编译时异常,使得程序在编译的时候就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
体会二:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对编译时异常,我们说一定要考虑异常的处理。
try-catch-finally中finally的使用:
二、throws+异常类型的使用
1、“throws+异常类型”写在方法的生命出。指明此方法执行时,可能会抛出异常类型。一旦方法执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会抛出。
2、体会:try-catch-finally:真正将异常给处理掉了
throws+异常类型:只是将异常抛给了方法的调用者,并没有将异常真正的处理掉。
开发时怎么选择两种异常处理方法:
①如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
②执行的方法A中,先后又调用了另外几个方法,这几个方法是递进关系执行的。建议这几个方法使用throws的方式进行处理,而方法A可以使用try-catch-finally
如何自定义异常类?
1、继承于现有的异常结构:RuntimeException、Exception
2、提供全局常量:serialVersionUID
3、提供重载的构造器
总结:
面试题:throw 和 throws的区别:
throw:表示抛出一个异常类的对象,生成异常对象的过程,声明在方法体内
throws:属于异常处理的一种方式,声明在方法的声明处