JAVA期末考试知识点总结
基础语法
在Java中,基本数据类型有以下几种:
1.整数类型:
2.byte:占用 1 字节(8 位),范围为 -128 到 127。
3.short:占用 2 字节(16 位),范围为 -32,768 到 32,767。
4.int:占用 4 字节(32 位),范围为 -2,147,483,648 到 2,147,483,647。
5.long:占用 8 字节(64 位),范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
6.浮点类型:
7.float:占用 4 字节(32 位),范围为约 3.4e-38 到 3.4e38,具有约7位有效数字。
8.double:占用 8 字节(64 位),范围为约 1.7e-308 到 1.7e308,具有约15位有效数字。
//注意要定位float必须在后面加上f,比如float t=3.14f; 如果不加f,默认为double类型的
9.字符类型:
10.char:占用 2 字节(16 位),用于表示一个Unicode字符,范围为 0 到 65,535。
11.布尔类型:
12.boolean:占用虚拟机相关的内存空间大小,可以表示 true 或 false。
这些基本数据类型都有对应的包装类,用于在需要对象时使用:
13.byte 的包装类是 Byte。
14.short 的包装类是 Short。
15.int 的包装类是 Integer。
16.long 的包装类是 Long。
17.float 的包装类是 Float。
18.double 的包装类是 Double。
19.char 的包装类是 Character。
20.boolean 的包装类是 Boolean。
这些包装类提供了许多有用的方法和功能,比如类型转换、数学运算、字符串转换等。可以通过这些包装类来处理基本类型的值。
基本变量定义
在Java中,变量定义的规则如下:
1.变量必须以字母、下划线(_)或美元符号($)开头。
2.变量名称可以包含字母、数字、下划线和美元符号。
3.变量名称区分大小写。
4.变量名称不能是Java关键字或保留字,如 int、class、static 等。
基本数据类型的定义可以分为两个步骤:声明和初始化。
声明变量的语法是:
dataType variableName;
其中,dataType 是基本数据类型,variableName 是变量名。
例如,声明一个整数类型的变量 num:
int num;
初始化变量的语法是将变量的值指定为一个初始值:
dataType variableName = initialValue;
其中,dataType 是基本数据类型,variableName 是变量名,initialValue 是初始值。
例如,声明并初始化一个整数类型的变量 num:
int num = 10;
你可以根据需要在适当的位置进行变量声明和初始化,也可以在声明变量的同时进行初始化。
请注意,Java还支持常量的定义,即使用关键字 final 定义的值不能被修改的变量。常量的定义方式与变量类似,但常量的命名通常以全大写字母加下划线分隔,如 PI、MAX_VALUE 等。例如:
final double PI = 3.14159;
final int MAX_VALUE = 100;
这些是变量定义和基本数据类型定义的基本规则和语法。根据需求,你可以根据这些规则定义合适的变量和使用合适的基本数据类型
数据类型转换,强转等
在Java中,基本数据类型的互相转换可以分为两种情况:自动转换(自动升级)和强制转换。
1.自动转换(自动升级):
2.当一个小的数据类型与一个大的数据类型进行运算或赋值时,会自动将小的数据类型转换为大的数据类型,这种转换称为自动转换或自动升级。
3.自动转换的规则按照数据类型的范围从小到大进行,以下是常见的自动转换规则:
4.byte -> short -> int -> long -> float -> double
5.char -> int
6.boolean 无法进行自动转换。
7.强制转换:
8.当一个大的数据类型需要转换为一个小的数据类型时,需要使用强制转换。强制转换可能会导致数据损失。
9.强制转换的语法是将目标数据类型放在括号中,并在括号前加上需要转换的变量。
10.示例:int num = (int) 3.14;
在不同类型之间进行计算时,Java会遵循以下规则:
11.如果参与计算的操作数中有 double 类型,那么其他操作数会自动提升为 double 类型。
12.如果参与计算的操作数中有 float 类型,那么其他操作数会自动提升为 float 类型。
13.如果参与计算的操作数中有 long 类型,那么其他操作数会自动提升为 long 类型。
14.对于整数类型(byte、short、int、long)之间的计算,如果两个操作数类型不同,那么较小的类型会自动提升为较大的类型。
15.如果参与计算的操作数中有 char 类型,那么其他操作数会自动提升为 int 类型。
需要注意的是,使用不同类型进行计算时,可能会发生数据溢出或精度丢失的情况。因此,在进行类型转换和计算时,需要注意选择适当的数据类型以及处理可能的数据范围和精度问题。
字符串的操作
在Java中,字符串是一个常用的数据类型,Java提供了许多用于操作字符串的方法和类。下面是一些常见的Java字符串操作:
使用字符串字面量创建字符串对象:String str = "Hello";
使用new关键字创建字符串对象:String str = new String("Hello");
使用length()方法获取字符串的长度,即包含的字符数:int length = str.length();
使用+运算符将两个字符串拼接起来:String newStr = str1 + str2;
使用contains()方法检查原字符串是否包含指定子字符串:boolean contains = str.contains("abc");
.使用substring()方法截取字符串的一部分:String subStr = str.substring(startIndex, endIndex);
.使用substring()方法截取从指定位置开始到字符串末尾的部分:String subStr = str.substring(startIndex);
.使用valueOf()静态方法将其他类型的数据转换为字符串:String str = String.valueOf(number);
条件语句
在Java中,条件控制语句用于根据条件来控制程序的执行流程,包括条件判断语句和循环语句。
条件判断语句:
if语句:根据给定的条件执行不同的代码块。
if (condition) {
// 执行当条件为真时的代码
} else if (condition2) {
// 执行当条件2为真时的代码
} else {
// 执行当上述条件都不满足时的代码
}
switch case语句:根据表达式的值匹配不同的case,并执行匹配到的代码块,可选地包含default子句。
switch (expression) {
case value1:
// 执行当expression等于value1时的代码
break;
case value2:
// 执行当expression等于value2时的代码
break;
// 其他case语句
default:
// 执行当上述case都不满足时的代码
break;
}
循环语句:
for循环:用于重复执行一段代码块,可指定初始化、条件和迭代语句。
for (initialization; condition; iteration) {
// 循环体代码
}
while循环:在指定条件为真时重复执行一段代码块。
while (condition) {
// 循环体代码
}
do-while循环:先执行一次循环体,再根据指定条件判断是否继续执行下一次循环。
do {
// 循环体代码
} while (condition);
在循环语句中,为了控制循环的执行流程,可以使用以下语句:
break:立即终止循环,执行循环后面的代码。
continue:跳过当前循环迭代,继续执行下一次循环。
使用条件控制语句和循环语句可以根据不同的条件和需求来控制程序的流程和重复执行特定的任务。根据具体情况选择合适的条件判断语句和循环语句来实现逻辑。
数组
在Java中,数组是一种用于存储多个相同类型元素的数据结构。以下是数组的声明、访问和遍历的方法:
1.数组声明:
2.声明数组需要指定数组类型和数组名,并可以指定数组的长度。语法如下:
java
dataType[] arrayName; // 声明一个数组变量
dataType[] arrayName = new dataType[arrayLength]; // 声明并初始化一个数组,指定数组长度
例如:
java
int[] numbers; // 声明一个整型数组变量
int[] numbers = new int[5]; // 声明并初始化一个长度为5的整型数组
3.数组访问:
4.数组的元素通过索引访问,索引从0开始,范围从0到数组长度减1。
5.使用数组名和索引来访问数组元素。语法如下:
java
arrayName[index]; // 访问指定索引的数组元素
例如:
java
int[] numbers = {1, 2, 3, 4, 5}; // 初始化一个整型数组
int element = numbers[2]; // 访问索引为2的数组元素,结果为3
6.数组遍历:
7.使用循环结构来遍历数组中的所有元素。
8.常用的循环结构有for循环和foreach循环。
9.for循环遍历数组需要使用数组的长度作为循环条件,通过索引访问数组元素。示例:
java
int[] numbers = {1, 2, 3, 4, 5}; // 初始化一个整型数组
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]); // 打印数组中的每个元素
}
10.foreach循环是一种简化的循环语法形式,可以直接遍历数组中的每个元素,不需要使用索引。示例:
java
int[] numbers = {1, 2, 3, 4, 5}; // 初始化一个整型数组
for (int number : numbers) {
System.out.println(number); // 打印数组中的每个元素
}
通过以上方法,可以声明、访问和遍历数组,对数组中的元素进行操作和处理。记得使用合适的索引和循环条件,确保不越界访问数组
面向对象内容
1.面向对象基本概念
学习面向对象的过程,实际上也是建立面向对象思维的过程。
先整体,再局部。先抽象,再具体,看透事物的本质,用java代码来解释某个事物的行为和特征,将其转变成一个可以被程序理解和调用的实例。
如何建立这种面向对象的思维呢?这里提出一个有趣的问题:这个世界是由什么组成的?
”如果是一个化学家,他也许会告诉你“这个世界是由分子、原子、离子等等的化学物质组成的”。
如果是一个画家呢?他也许会告诉你,“这个世界是由不同的颜色所组成的”。…每个人肯定都有自己的看法,这里就不一一赘述了。
但如果让一个分类学家来考虑问题就有趣的多了,他会告诉你“这个世界是由不同类型的物与事所构成的”好!作为面向对象的程序员来说,我们要站在分类学家的角度去考虑问题!是的,这个世界是由动物、植物等组成的。动物又分为单细胞动物、多细胞动物、哺乳动物等等,哺乳动物又分为人、大象、老虎……就这样的分下去了!
有了这种考虑问题的思维之后,我们来分析一下,什么是人类 ?
首先让我们来看看人类所具有的一些特征,这个特征包括属性(一些参数,数值)以及方法(一些行为,他能干什么!)。每个人都有身高、体重、年龄、血型等等一些属性。人会劳动、人都会直立行走、人会合理的使用工具等等这些方法。
人之所以能区别于其它类型的动物,是因为每个人都具有人类这个群体的属性与方法。“人类”只是一个抽象的概念,它仅仅是一个概念,所有具备“人类”这个群体的属性与方法的对象都叫人!
比如说 亚当、夏娃是具体的人,属于人类,因为他们具有人类的行为和特征。
2.类和对象的关系
定义一个类的对象,其实就是创建一个类的实例,表示一个具体的对象。
格式: 类名 对象名 = new 类名();
例如: 人类 亚当 = new 人类();
左边的这个人类,表示亚当的类型是人类,右边的 new 人类(); 表示亚当是一个具体的实例,拥有人类的行为和特征。
在声明一个具体对象时,如果只声明对象而没有给出具体实例,例如
People p1 =null; 或者 People p2 ;
此时p1 和p2 无法正常使用,因为没有具体的内存指向。
p1 = new People(); p2 = new People();
在有了具体的内存指向后,p1和p2 才可以正常使用。
3类中的成员
类的定义
class 类名 {
(特征)属性名称
例如: int age ; //年龄
(行为)方法名(){ }
例如: void move(){ 直立行走 }
}
在类中描述人类特征的,就是类中的属性,也叫成员属性。在类中描述人类行为的,就是类中的方法,也叫成员方法。
如果要想访问类中的属性或方法则可以依靠以下的语法形式:
访问类中的属性:对象名.成员属性 ;
调用类中的方法:对象名.成员方法();
public class People {
//类中的成员属性
int age;//年龄
String name;//名字
double lenght;//身高
//类中的成员方法
public void move() {//移动
System.out.println("直立行走");
}
public void sleep() {
System.out.println("日出而作日落而息");
}
public static void main(String[] args) {
//创建类的实例对象 可以调用类中的属性和方法
People p1 = new People();//
p1.name = "亚当";
p1.age=30;
p1.lenght=1.85;
System.out.println("这个人类的名字是"+p1.name);
System.out.println("这个人类的年龄是"+p1.age);
System.out.println("这个人类的身高是"+p1.lenght+"m");
p1.move();
p1.sleep();
}
}
这个人类的名字是亚当
这个人类的年龄是30
这个人类的身高是1.85m
直立行走
日出而作日落而息
4.类的构造器
类的构造器,也称为构造方法,每个类中都默认存在一个,与类名完全相同,没有返回值类型修饰符(包括void),没有参数的方法。
public class People {
public People() {
//自动产生的构造方法
}
构造方法的作用,是给类中的成员属性初始化赋值。在类中不允许先声明变量后赋值的情况,一般情况下,都是使用构造方法来给属性赋值,这个过程就是初始化。
构造方法也是类中的方法,支持方法的重载。即方法名相同,参数列表不同。同一个类中可以有多个构造方法,在创建类的对象时, new 类名 () ,这个()实际上就是一个无参构造。也就是说,在创建类的实例对象时,实际上是执行了该类的构造方法。
public class People {
public People() {
System.out.println("我是People类构造器");
}
public static void main(String[] args) {
People p1 = new People();//执行
}
}
注意:当在类中写了有参构造方法后。默认的无参构造将不再生效,创建类的实例对象时,需要根据构造方法传递对应的参数,否则就会报错。
(1)构造方法名称与类名相同,没有返回值声明(包括 void)
(2)构造方法用于初始化数据(属性)
(3)每一个类中都会有一个默认的无参的构造方法
(4)如果类中写了构造方法,那么默认构造方法将无效
(5)如果写了有参构造方法,还想保留默认构造方法,需要显示的写出来。
(6)构造方法可以有多个,但参数不一样,称为构造方法的重载
(7)在构造方法中调用另一个构造方法,使用this(…),该句代码必须在第一句。
(8)构造方法之间的调用,必须要有出口,否则会保错。
(9)给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留。
(10)一个好的编程习惯是要保留默认的构造方法。(为了方便一些框架代码使用反射来创建对象)。
5堆和栈
创建类的对象 : 类名 对象名 = new 类名();
如果输出这个对象名的话,看到输出的是一串乱码,其实这是这个对象在内存中的地址值。
对象名可以理解为一个引用,储存在栈中。
创建的实例对象 new People是储存在堆中,包含类中全部的属性和方法。
对象名.属性 这个调用属性的过程,实际上就是通过引用地址,找到堆内存中对应数据的过程。
6关键字this的作用
注意 1.this不能出现在普通方法中,必须出现在构造方法里面
2.必须是构造方法中的第一条语句,构造方法之间的调用必须留出口。否则就是死循环,会报错。
7 静态关键字static
static:静态修饰符
使用static关键字修饰一个属性:该属性就是静态属性,也叫全局变量,类名.属性名可以直接访问
使用static关键字修饰一个方法:该方法就是静态方法,可以不用创建对象,直接类名.方法名调用
声明为static的方法有以下几条限制:
1、静态方法只能调用其他的静态方法,不能调用非静态方法。而在非静态方法中是可以调用静态方法的
2、静态方法中只能访问静态属性
3、静态方法不能引用this或super。
4、不允许用来修饰局部变量
public class People {
static int a;//静态属性
int b;//非静态属性
static void m1() {//静态方法
a=100;
//b=10;//编译错误,不能访问非静态属性
//m2();//编译错误,不能调用非静态方法
m3();
}
void m2() {//非静态方法
b=10;
a=100;
m3();
}
static void m3() {
}
封装
实际上就是信息隐藏,将类中的成员属性和成员方法修饰为私有化,数据被保护在对象的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系,即get/set方法。
其他对象只能通过该对象提供的get/set方法,与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节(当然也无从知道),但可以通过该对象对外提供的接口来访问该对象。
对于封装而言,一个对象它所封装的是自己的属性和方法,所以它不需要依赖其他对象就可以完成自己的操作。使用封装的好处:
1、良好的封装能够减少耦合。
2、类内部的结构可以自由修改。
3、可以对成员进行更精确的控制。
4、隐藏信息,实现细节。
列子
public static void main(String[] args) {
Dog d1 = new Dog();
d1.age=3;
d1.name="旺财";
}
}
class Dog{
public int age;
public String name;
}
如果有很多代码都使用了Dog这个类;当某一天这个类的age属性需要换成String类型,那么,外部使用它的任何地方都需要需改xxx.age=“xxx”,这将是非常繁琐的一个过程,那该怎么办呢?很简单,使用private修饰符将属性封装,开放访问接口的方法,我们只需要修改一下set方法就能完美解决
public static void main(String[] args) {
Dog d1 = new Dog();
//调用时发生变化
d1.setName("旺财");
d1.setAge(3);
}
}
class Dog{
private String age;//修改为String类型
private String name;
//将属性私有化,提供set方法,将int类型的值转成String
public void setAge(int age) {
this.age = String.valueOf(age);
}
public void setName(String name) {
this.name = name;
}
这样外部使用它的地方都不用修改,我们只用简单的修改对象内部就可以了,更加方便快捷。到了这里我们应该可以看出,封装确实可以使我们容易地修改类的内部实现,而无需修改使用了该类的客户代码。
这里还可以体现出一些封装属性的优势,案例如下:
public static void main(String[] args) {
Dog d1 = new Dog();
d1.setName("旺财");
d1.setAge(300);//狗的年龄赋值很明显不合理,这里就需要在set方法中给出提示
}
}
class Dog{
private String age;
private String name;
public void setAge(int age) {
if(age>100||age<0) {
System.out.println("你见过超过100岁的狗狗吗?");
}else {
this.age = String.valueOf(age);
}
}
public void setName(String name) {
this.name = name;
}
}
8 特性 继承
继承,通俗点来说就是 子承父业。是使用已存在的类的定义作为基础,建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类中的某些属性,除了父类中私有的属性和方法,子类必须全部继承。
1、被继承的类称为父类(超类),继承父类的类称为子类(派生类)使用 关键字extends 继承。
2、子类拥有父类非 private 的属性、方法。
3、子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
4、子类可以用自己的方式实现父类的方法(即方法的重写/覆盖)。
5、构造器而言,它只能够被调用,而不能被继承,子类可以使用super()调用父类构造器。
6、对于继承而已,子类会默认调用父类的无参构造,但是如果父类没有无参构造,子类必须要在其构造方法中的第一行代码调用指定父类的构造器,传递对应参数。
7、Java 的继承是单继承,但是可以多重继承,即子类只能拥有一个直接父类,但是该父类仍然可以作为其他类的子类。
class Father{
//父类中的非私有属性都会被继承
public int age;
private double money;
String home;
//父类中的非私有方法都会被继承
void eat() {} //吃东西
public void sleep() {}//睡觉
private void soner() {}//打呼噜
//父类构造方法如果有参数,子类必须在构造方法第一行中调用
public Father(int x) {
//父类如果写了有参构造,那么默认的无参构造将不再生效
}
}
class Son extends Father{
public Son(int x) {
super(x);//调用父类构造 super()
age=12;//继承后拥有父类的属性
home="王者峡谷河道下边的草丛";
//money=3.0; 父类私有属性无法访问
sleep();//可以执行父类中的非私有方法
}
}
这里补充一下,子类继承父类,默认在子类构造方法中调用父类构造,在创建子类实例对象时,实际上的执行顺序是(父类构造——子类构造)。在类的构造器中还有一段特别的代码,优先与构造器,在创建对象时优先执行,话不多说直接看代码:
public static void main(String[] args) {
Z z1 = new Z();//创建子类实例对象时,实际上的执行顺序
}
}
class F {
static {
//静态代码块,只有方法的大括号,没有方法名返回值等任何内容
System.out.println("父类静态代码块");
}
{
//构造代码块,只有方法的大括号,没有方法名返回值等任何内容
System.out.println("父类构造代码块");
}
public F() {
// 父类构造器
System.out.println("父类构造器");
}
}
class Z extends F{
static {
//静态代码块,只有方法的大括号,没有方法名返回值等任何内容
System.out.println("子类静态代码块");
}
{
//构造代码块,只有方法的大括号,没有方法名返回值等任何内容
System.out.println("子类构造代码块");
}
public Z() {
// 父类构造器
System.out.println("子类构造器");
}
}父类静态代码块
子类静态代码块
父类构造代码块
父类构造器
子类构造代码块
子类构造器
9.多态
面向对象三大特征,封装、继承、多态。从某种意义上来讲,封装和继承几乎就是为了多态而准备的,也是三大特征中最重要的知识点。
多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。简单来说就是不同类型的对象(父类或子类)调用同一个方法,根据发出调用的对象不同,执行的方法也就不同。
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态的作用:消除类型之间的耦合关系。
这里举一个简单的小例子:父类是个农民,技能是使用锄头耕地。子类继承了父类,重写了父类的锄头耕地技能,更新换代为使用拖拉机耕地。如果是父类对象调用这个技能,就是使用锄头手动耕地,如果是子类对象调用这个技能,就是使用拖拉机耕地。
java实现多态有三个必要条件:继承、重写、父类引用指向子类对象。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
父类引用指向子类对象(向上转型):在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
举列
public class People {//人类 作为所有职业的父类
public int age;
public String name;
void work() {//父类的方法
System.out.println("劳动");
}
public static void main(String[] args) {
//父类引用指向子类对象就是多态性的体现
People p1 = new Doctor();// p1的类型是人类类型,但是指向的实例对象是医生
p1.work();//父类引用发出的调用,调用到的是医生类中重写父类的方法
People p2 = new Teacher();
p2.work();//父类引用发出的调用,调用到的是教师类中重写父类的方法
}
}
class Doctor extends People{
@Override
void work() {//医生类继承人类,重写工作方法
System.out.println("救死扶伤");
}
}
class Teacher extends People{
@Override
void work() {//教师类继承人类,重写工作方法
System.out.println("教书育人");
}
}
多态的好处:
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如:医生、律师、程序员都是人类的子类,根据使用场景不同随时可以替换为符合的职业。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在拥有了医生、律师的继承上,还可以继续添加新的职业,比如主播,运动员等,都是添加为人类的多态性。
3.接口性(interface-ability)。多态是超类通过抽象方法,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。每个子类都可以根据自身的特性去重写父类的抽象方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
10重载和重写
方法的重载与重写就是方法多态性的一种表现。
重载的规则:
1、必须具有不同的参数列表;
2、可以有不同的返回类型,只要参数列表不同就可以了;
3、可以有不同的访问修饰符;
4、可以抛出不同的异常;
重写方法的规则:
1、参数列表必须完全与被重写的方法相同。
2、返回的类型必须一直与被重写的方法的返回类型相同。
3、访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
4、重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。例如:
父类的一个方法申明了一个检查异常IOException,在重写这个方法是就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常
5、父类的私有方法不能被子类继承,所以不能被重写
6、父类中的方法若使用private、static、final任意修饰符修饰,那么,不能被子类重写。
11 super关键字的作用
可以理解为对父类的引用,super可以完成以下的操作:
1、使用super调用父类中的属性,可以从父类处获得信息。
2、使用super调用父类中的方法,在子类中执行。
3、使用super调用父类中的构造方法(super(实参)形式),必须在子类构造方法的第一条语句,调用父类中相应的构造方法,若不显示的写出来,默认调用父类的无参构造方法,比如:super();
class F {
public int age;
public String name;
void m1() {
System.out.println("父类方法");
}
public F(int age) {
System.out.println("父类有参构造");
}
public F(){
System.out.println("父类无参构造");
}
}
class Z extends F{
public Z() {
super();//调用父类构造,若不写这行代码也会默认调用父类的无参构造
//若是调用父类有参构造,则需要传递对应类型的参数
super.age=30;//调用父类属性
super.m1();//调用父类方法
}
}
12final关键字的作用
使用final关键字完成以下的操作:
a、使用final关键字声明一个常量
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化。
public final int age =10;
//声明为常量时必须赋值,否则会报错
public static final String name = "亚当";
//一般和静态修饰符配合使用,修饰为静态常量,
//在编译期间知道静态常量的确切值,所以编译器会把它当做编译期常量使用进行优化,字体变成蓝色加粗
void work() {//父类的方法,此方法可以被子类重写
System.out.println("劳动");
}
final void sleep() {
//此方法不能被子类重写,但是可以被子类调用
}
a.如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的
b、使用final关键字声明一个方法,该方法为最终方法,且只能被子类继承,但是不能被子类重写。
c、使用final关键字声明一个类,该类就转变为最终类,没有子类的类,fianl修饰的类无法被继承。
13接口
Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。接口实现和类继承的规则不同,为了数据的安全,继承时一个类只有一个直接父类,也就是单继承,但是一个类可以实现多个接口,接口弥补了类的不能多继承缺点,继承和接口的双重设计既保持了类的数据安全也变相实现了多继承。
Java接口本身没有任何实现,因为Java接口不涉及表象,而只描述public行为,所以Java接口比Java抽象类更抽象化。但是接口不是类,不能使用new 运算符实例化一个接口。如 x=new comparable(…);//这个是错误来的。但是可以声明接口变量Comparable x; //这是允许的。Java接口的方法只能是抽象的和公开的,Java接口不能有构造器,Java接口可以有public、static和final属性。即接口中的属性可以定义为 public static final int value=5;
public interface Weapon {//声明一个接口
int HP=100;//接口中定义的变量就是静态常量
void kill();//接口中定义的方法就是抽象方法
//当不定义返回值类型时默认public
//private void m1();//接口中不能定义私有抽象方法,没有意义
//private int MP=100;//接口中不能定义私有属性
static void m2() { //接口中可以有具体的方法,必须声明为static或者default
System.out.println("物理攻击");
}
}
接口的使用规则:
1、定义一个接口,使用interface关键字
2、在一个接口中,只能定义常量、抽象方法,JDK1.8后可以定义默认的实现方法
3、接口可以继承多个接口:extends xxx,xxx
public interface Weapon2 extends Weapon,Weapon1{
//接口可以继承接口,可以继承多个接口
}
一个具体类实现接口使用implements关键字,一个类只能有一个父类,但是可以实现多个接口
class Hero implements Weapon,Weapon2{
//一个类只能继承一个父类,但是可以实现多个接口
//使用implements关键字,以逗号隔开。
//非抽象类实现接口必须重写接口中的抽象方法
@Override
public void kill() {
// TODO Auto-generated method stub
}
}
5、抽象类实现接口可以不实现接口的方法,因为二者都是抽象的
6、在接口中定义的方法没有声明 访问修饰符,默认为public
7、接口不能有构造方法
8、接口不能被实例化,不能new 接口
14instanceof关键字的作用
instanceof是Java的一个二元操作符(运算符),也是Java的保留关键字。它的作用是判断其左边对象是否为其右边类的实例,返回的是boolean类型的数据。用它来判断某个对象是否是某个Class类的实例。
使用方法:object instanceof class
object :必选项。任意引用对象。
class:必选项。任意已定义的对象类。
说明:如果该object 是该class的一个实例,那么返回true。如果该object 不是该class的一个实例,或者object是null,则返回false。
15抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,那么这样的类就是抽象类。abstract class
抽象类往往用来对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
抽象类具有以下特性:
抽象类不能实例化。
抽象类中可以没有抽象方法,但是有抽象方法的类一定是抽象类
public abstract class People {
//由abstract修饰符修饰的类就是抽象类
abstract void work();
//由abstract修饰符修饰的方法就是抽象方法,没有具体的方法执行,即方法体{}
}
class Doctor extends People{
// 非抽象的子类继承抽象父类,必须重写父类中的抽象方法
@Override
void work() {//子类重写的方法必须有方法执行体{}
System.out.println("救死扶伤");
}
}
不能用 final修饰符修饰抽象类,因为这两个修饰符的含义是相反的。 采用 final修饰符的类无法继承,而 abstract 修饰符要求对类进行继承。
从抽象类派生的非抽象类必须包括继承的所有抽象方法,并且实际的实现该父类中的抽象方法。
抽象类不能直接实例化,并且对抽象类使用 new 运算符会导致编译时错误。虽然一些变量和值在编译时的类型可以是抽象的,但是这样的变量和值必须或者为 null,或者含有对非抽象类的实例的引用(此非抽象类是从抽象类派生的)。
public static void main(String[] args) {
//People p1 =new People();//编译错误,抽象类不能实例化
People p2 = new Doctor();//编译正确
}
在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现,则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。
16object类中的方法有
Object类是Java中所有类的顶级父类,它在Java的类层次结构中处于最顶端。Object类的作用是为所有其他类提供通用的方法和功能。以下是Object类中常见方法的作用:
1.equals(Object obj):用于判断当前对象是否与给定对象相等。默认情况下,它使用的是对象引用的比较,但可以通过在子类中重写此方法来自定义相等的判断条件。
2.hashCode():返回对象的哈希码值。哈希码用于在哈希表等数据结构中快速定位对象。默认实现是根据对象的内部地址生成哈希码,但也可以在子类中重写此方法以根据对象的内容生成哈希码。
3.toString():返回对象的字符串表示。默认实现返回类名和对象的哈希码。通常可以通过在子类中重写此方法来提供更有意义的字符串表示。
4.getClass():返回对象的运行时类的Class对象。可以使用该方法获取对象所属的具体类信息。
5.clone():创建并返回当前对象的一个副本。默认情况下,该方法会进行浅拷贝(对于对象的引用类型成员变量,只会复制引用而不会复制整个对象)。可以在实现了Cloneable接口的类中重写此方法来实现深拷贝。
6.finalize():在对象被垃圾回收器回收之前调用。这个方法可以被子类重写以执行对象释放资源等清理操作。
7.notify() 和 notifyAll():用于与线程的等待/通知机制配合使用。notify方法唤醒正在等待该对象的某个线程,而notifyAll方法唤醒所有正在等待该对象的线程。
8.wait() 和 wait(long timeout):使当前线程进入等待状态,直到其他线程调用notify或notifyAll方法唤醒它,或者等待时间超过指定的timeout。
这些方法是Object类中的常见方法,它们提供了一些基本的对象操作和与多线程相关的功能。其他还有一些方法如wait(long timeout, int nanos)、getClassLoader()等更专用或用于特定用途的方法,可以根据具体需求来使用。
异常机制
在Java中,异常(Exception)是指在程序执行过程中遇到的问题或错误情况。Java提供了一种异常处理机制,使程序能够捕获和处理这些异常以避免程序的异常终止。
1.异常分为两种类型:
2.受检异常(Checked Exception):也称为编译时异常,是指在Java程序中必须显式处理的异常。这些异常是由于外部环境或底层库的问题导致的,比如文件不存在、网络连接中断等。受检异常在方法声明中需要使用throws关键字声明,或者使用try-catch语句捕获并处理。
3.非受检异常(Unchecked Exception):也称为运行时异常,是指在程序运行过程中可能出现的异常情况。这些异常通常是由程序错误导致的,比如空指针引用、数组越界等。非受检异常不需要在方法声明中使用throws关键字声明,也可以使用try-catch语句捕获并处理。
4.异常处理机制的作用:
5.异常处理机制允许程序员捕获和处理可能发生的异常,以使程序在异常情况下继续执行,而不是直接终止。异常处理的目标是增强程序的鲁棒性和可靠性。
6.通过捕获和处理异常,程序可以提供有用的错误信息或进行恰当的处理操作,例如打印错误日志、关闭资源、进行回滚操作等。
7.try-catch、throw、throws的使用区别:
8.try-catch语句用于捕获和处理异常。放置可能抛出异常的代码块(try块)后面跟随一个或多个catch块,用于处理特定类型的异常。语法如下:
java
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 处理ExceptionType1类型异常的代码
} catch (ExceptionType2 e2) {
// 处理ExceptionType2类型异常的代码
} finally {
// 可选的finally块,无论是否发生异常都会执行的代码
}
9.throw关键字用于在程序中手动抛出异常。可以创建一个异常对象,然后使用throw语句将异常抛出。语法如下:
java
throw exception; // 抛出指定的异常
10.throws关键字用于在方法签名中声明可能抛出的受检异常。当方法内部可能抛出受检异常时,可以在方法声明中使用throws关键字指定异常类型。调用该方法时,调用方要么捕获这些异常,要么继续向上层方法抛出。语法如下:
java
returnType methodName() throws ExceptionType1, ExceptionType2 {
// 可能抛出异常的代码
}
总结:
11.异常是Java中处理错误和异常情况的机制。
12.受检异常是必须显式处理的异常,非受检异常是不需要显式处理的异常。
13.异常处理机制的作用是捕获和处理程序执行过程中的异常,保证程序的可靠性。
14.try-catch用于捕获和处理异常,throw用于手动抛出异常,throws用于在方法声明中声明可能抛出的异常。
集合类
collection是顶层接口。
集合类是Java中用于存储和操作一组对象的容器类。它提供了一系列的方法来添加、删除、访问和遍历集合中的元素。常用的集合类包括List、Set和Map。
1.List(列表):
2.List是一个有序的集合,允许存储重复的元素。
3.List可以通过索引访问元素,提供了按照索引插入、删除和替换元素的操作。
4.常见的List实现类有ArrayList、LinkedList和Vector。
5.例如:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
System.out.println(list.get(0)); // 访问元素
for (String element : list) {
System.out.println(element); // 遍历元素
}
6.Set(集):
7.Set是一个不允许存储重复元素的集合。
8.Set没有定义特定的顺序,元素的存储顺序可以是无序的。
9.常见的Set实现类有HashSet、LinkedHashSet和TreeSet。
10.例如:
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
System.out.println(set.contains(2)); // 访问元素
for (Integer element : set) {
System.out.println(element); // 遍历元素
}
11.Map(映射):
12.Map是一个键值对的集合,每个元素存储为键和值的二元组。
13.Map不允许重复的键,但允许不同键对应相同的值。
14.常见的Map实现类有HashMap、LinkedHashMap和TreeMap。
15.例如:
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
System.out.println(map.get("B")); // 访问元素
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue()); // 遍历元素
}
对于访问和遍历集合类的操作,通常可以使用以下方法:
16.通过索引访问List中的元素,通过get方法获取指定索引的元素。
17.使用contains方法判断Set中是否包含某个元素。
18.使用get方法或通过键来访问Map中的值。
19.使用增强的for循环(foreach循环)来遍历集合元素。
总结:
20.List是有序的集合,允许存储重复元素。
21.Set是不允许存储重复元素的集合,没有特定的顺序。
22.Map是键值对的集合,键不允许重复,值可以重复。
23.访问和遍历集合可以使用索引、contains方法和键来获取元素,也可以使用增强的for循环遍历集合元素。
io流
//期末的内容
io按方向 分为输入流和输出流,按处理内容分为 字节流和字符流 按接入不同 分为 节点流和处理流。
Java中的流(Stream)是用于数据输入和输出的抽象概念。流可以用于读取或写入文件、网络连接、内存等地方的数据。在Java中,流分为字节流和字符流两种类型。
1.字节流(Byte Stream):
2.字节流以字节为单位进行读写操作,适用于处理二进制文件(如图片、视频等)或任意类型的文件。
3.常见的字节流类有InputStream和OutputStream以及它们的子类,如FileInputStream、FileOutputStream等。
4.例如,使用字节流读取文本文件的内容:
java
FileInputStream fis = new FileInputStream("path/to/file.txt");
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
fis.close();
5.字符流(Character Stream):
6.字符流以字符为单位进行读写操作,适用于处理文本文件,能够处理Unicode字符集。
7.常见的字符流类有Reader和Writer以及它们的子类,如FileReader、FileWriter等。
8.例如,使用字符流读取文本文件的内容:
java
FileReader fr = new FileReader("path/to/file.txt");
int c;
while ((c = fr.read()) != -1) {
System.out.print((char) c);
}
fr.close();
字节流和字符流之间可以进行转换,主要通过使用字节流来构造字符流。Java提供了几种转换流类用于实现字节流和字符流的转换。
9.InputStreamReader:
10.InputStreamReader是从字节流到字符流的桥梁,它将字节流转换为字符流。
11.可以使用InputStreamReader构造函数将字节流对象传递给它,然后使用该对象进行字符流的读取操作。
12.例如,将字节流转换为字符流读取文本文件的内容:
java
FileInputStream fis = new FileInputStream("path/to/file.txt");
InputStreamReader isr = new InputStreamReader(fis);
int c;
while ((c = isr.read()) != -1) {
System.out.print((char) c);
}
isr.close();
13.OutputStreamWriter:
14.OutputStreamWriter是从字符流到字节流的桥梁,它将字符流转换为字节流。
15.可以使用OutputStreamWriter构造函数将字节流对象传递给它,然后使用该对象进行字节流的写入操作。
16.例如,将字符流转换为字节流写入文本文件:
java
FileOutputStream fos = new FileOutputStream("path/to/file.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("Hello, World!");
osw.close();
注意:在使用字节流和字符流进行读写操作时,需要注意字符编码的设置,以确保正确地处理文本内容的字符集。
总结:
17.字节流适用于处理二进制文件和任意类型的文件,以字节为单位进行读写操作。
18.字符流适用于处理文本文件,以字符为单位进行读写操作,能够处理Unicode字符集。
19.使用InputStreamReader和OutputStreamWriter可以实现字节流和字符流之间的转换。
序列化和反序列化
序列化是指将对象转换为字节序列的过程,使得对象可以在网络上传输或者永久保存到硬盘上。反序列化则是将字节序列重新转换为对象的过程。
在Java中,实现序列化需要遵循以下步骤:
让类实现java.io.Serializable接口。这是一个标记接口,没有任何方法需要实现,仅用于标识可序列化的类。
在类中标记需要序列化的字段为transient(瞬态)或实现java.io.Serializable接口。瞬态字段不会被序列化,只有实现了Serializable接口的字段才会被序列化。
使用java.io.ObjectOutputStream类将对象序列化为字节流。可以使用FileOutputStream来包装ObjectOutputStream以将字节流写入文件中。
使用java.io.ObjectInputStream类将字节流反序列化为对象。可以使用FileInputStream来包装ObjectInputStream以从文件中读取字节流。
需要注意的地方有:
序列化仅保存对象的状态,不保存类的成员方法、静态变量和transient修饰的字段。
如果类中包含了对其他对象的引用,那么这些对象也必须是可序列化的,否则会抛出java.io.NotSerializableException。
序列化和反序列化的类的完全限定名必须相同,否则会抛出java.io.InvalidClassException。
序列化和反序列化时,需要注意对象引用的一致性。如果多个对象引用同一个对象,在反序列化后恢复对象时,依然会维持这个关联关系。
静态变量不会被序列化,因为它属于类而不是对象。反序列化后,静态变量将使用类定义时的值。
继承关系下的注意事项:
被继承的父类如果实现了java.io.Serializable接口,子类也会自动实现该接口,无需显式声明。
子类的字段会继承父类的字段,并且一同被序列化和反序列化。
在父类和子类中,如果有对字段的重定义(字段名相同),则子类中的字段值会被序列化和反序列化,而父类中的字段值不会被序列化和反序列化。
需要确保父类和子类的序列化版本号一致,可以使用private static final long serialVersionUID字段进行版本控制。
总结:
序列化是将对象转换为字节序列的过程,实现序列化需要实现Serializable接口,并使用ObjectOutputStream和ObjectInputStream进行序列化和反序列化。注意要点包括标记可序列化的字段、处理对象引用的一致性,以及继承关系下的版本控制和字段继承问题。
线程
在Java中,可以使用两种方式实现多线程:通过继承Thread类或者通过实现Runnable接口。下面是这两种方式的示例:
通过继承Thread类实现多线程:
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的逻辑
// ...
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
通过实现Runnable接口实现多线程:
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的逻辑
// ...
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
这两种方式都可以创建多个线程并同时执行不同任务。
关于多线程的状态图,Java中的线程有以下四种状态:
New(新建状态):线程对象已经创建,但尚未调用start()方法。
Runnable(可运行状态):线程已经启动,等待CPU分配时间片执行任务。
Blocked(阻塞状态):线程被阻塞,无法继续执行。常见的阻塞原因包括等待输入/输出、获取锁、等待其他线程的通知等。
Terminated(终止状态):线程执行完毕或者因异常而终止。
下面是这些状态之间的转换关系:
New --> Runnable : 线程对象调用 start() 方法启动线程
Runnable --> Blocked : 线程请求锁、等待输入/输出、等待其他线程通知等
Blocked --> Runnable : 线程获取到所请求的锁、输入/输出准备好、其他线程通知该线程等
Runnable --> Terminated : run() 方法执行完毕或者因异常终止
需要注意的是,Java中的线程调度是由操作系统决定的,因此线程的切换和状态转换并不是确定的。上述状态图描述的是通常情况下的状态转换过程,实际执行中可能会有一些变化。
java事件处理
Java事件处理模型是一种机制,用于处理用户界面或其他应用程序中发生的事件。它允许我们编写代码来响应用户的操作,例如点击按钮、键盘输入、鼠标移动等。
Java事件处理模型由以下几个部分组成:
1.事件源(Event Source):事件源是生成事件的对象。例如,按钮(Button)、文本字段(TextField)等都可以是事件源。当事件源上发生用户操作时,比如点击按钮,它将生成一个事件。
2.事件对象(Event Object):事件对象封装了事件的信息。它包含了事件源、事件类型和其它相关的数据。在事件源上发生事件时,创建相应的事件对象并传递给事件监听器。
3.事件监听器(Event Listener):事件监听器是一个接口,我们可以实现该接口来定义事件处理代码。它包含一个或多个用于处理不同类型事件的回调方法。当事件发生时,事件源会调用适当的事件监听器方法。
4.注册(注册):通过注册,将事件监听器与特定的事件源关联起来。这样,当事件源上发生相应的事件时,事件监听器就会被调用。
5.事件分派(Event Dispatching):事件分派是指将事件对象传递给注册的事件监听器进行处理的过程。当事件发生时,事件源将事件对象传递给相应的事件监听器。事件监听器执行逻辑来响应事件,可能会修改应用程序的状态或执行特定的操作。
通过这些组件的协作,Java事件处理模型实现了一种可扩展、灵活的方式来处理用户交互和产生的事件。开发人员可以根据需要注册监听器,定义自己的事件处理逻辑,并在事件发生时触发相应的操作。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!