今天我们继续复习与JavaSE相关的知识,使用的编译器仍然是IDEA2022,大家伙使用eclipse或其他编译环境是一样的,都可以。
目录
数组
定义
一维数组
编辑
二维数组
多维数组
数组的遍历
for循环遍历
编辑
foreach遍历
封装、继承和多态
封装
继承
多态
重写
instanceof关键字
final关键字
抽象类
接口
总结
数组
定义
数组是相同类型数据的有序集合。数组可以代表任何相同类型的一组内容(包括引用类型和基本类型)其中存放的每一个数据称为数组的一个元素,数组的下标是从0开始,也就是第一个元素的索引是0,不是基本数据类型。
一维数组
一维数组中,元素是依次排列的(线性),每个数组元素可以通过下标来访问。声明方式如下:
- 类型[] 变量名称 = new 类型[数组大小];
- 类型[] 变量名称 = new 类型[数组大小];
- 类型[] 变量名称 = new 类型[]{...}; //静态初始化(直接指定值和大小)
- 类型[] 变量名称 = {...};
public class Main {
public static void main(String[] args) {
int[] arr_1 = new int[10];
float[] arr_2 = new float[10];
int[] arr_3 = new int[]{1,1,4,5,1,4};
double[] arr_4 = {1.0,2.3,4.9};
}
}
创建出来的数组每个元素都有默认值,我们可以通过下标去访问。
public class Main {
public static void main(String[] args) {
int[] arr = new int[10];
arr[0] = 626;
System.out.println(arr[0]);
System.out.println(arr[1]);
}
}
我们可以通过数组变量名称.length来获取当前数组长度:
public class Main {
public static void main(String[] args) {
int[] arr_3 = new int[]{1,1,4,5,1,4};
System.out.println(arr_3.length);
}
}
数组在创建时,就固定长度,不可更改。访问超出数组长度的内容,会出现错误。例如在Java当中,如果你尝试访问超出数组长度的内容,程序会抛出ArrayIndexOutOfBoundsException
异常。
public class Main {
public static void main(String[] args) {
int[] arr_3 = new int[]{1,1,4,5,1,4};
System.out.println("length of arr_3" + "=" + arr_3.length);
System.out.println(arr_3[10]);
}
}
二维数组
二维数组是存放数组的数组,每一个元素都存放一个数组的引用,等于在数组当中嵌套一个数组,套娃中的套娃,可以类比线性代数当中的矩阵或行列式来理解。以下就是一个典型的二维数组:
int[][] arr = { {1, 2},
{3, 4},
{5, 6}};
System.out.println(arr[2][1]);
二维数组的定义方式与一维数组几乎完全一致,如下所示:
- 类型[][] 变量名称 = new 类型[数组大小][数组大小];
- 类型[][] 变量名称 = new 类型[数组大小][数组大小];
- 类型[][] 变量名称 = new 类型[][]{...}; //静态初始化(直接指定值和大小)
- 类型[][] 变量名称 = {...};
public class Main {
public static void main(String[] args) {
int[][] arr_1 = new int[10][10];
float[][] arr_2 = new float[10][10];
int[][] arr_3 = new int[][]{{1,1},{4,5},{1,4}};
double[][] arr_4 = {{1.0,2.3},{4.9,5.2}};
}
}
当然,这里我们也可以用length来查看数组的长度,方法与一维数组一样。
多维数组
多维数组与二维数组类似,只是不断的套娃而已,可以借鉴二维数组的原理来理解。
数组的遍历
for循环遍历
for循环是我们马上就能想到的一种遍历方法,从我们学习C语言开始,我们就知道想要遍历一个数组,最直接的方法就是使用for循环来遍历。同样,在Java当中,for循环他也是存在的,格式也一样,for循环直呼:没想到吧!下面是一个常规的for循环:
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,1,4,5,1,4};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
foreach遍历
foreach属于增强型的for循环,它使得代码更简洁,同时我们能直接拿到数组中的每一个数字。它隐藏了迭代器和索引的细节,使得遍历数组或集合更加直观。但是,它不允许你直接访问元素的索引。
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,1,4,5,1,4};
for (int i : arr) {
System.out.println(i);
}
}
}
封装、继承和多态
封装、继承和多态是面向对象编程的三大特性。
封装
封装的主要目的是保护对象的内部状态,防止外部代码直接访问对象的内部属性,并通过定义公共的方法来访问这些属性。这样做的好处包括提高代码的安全性、可维护性和灵活性。封装的作用主要可以概括为以下五点:数据隐藏、提高代码的安全性、提高代码的可维护性、灵活性、促进模块化。
如下我为大家展示了一段经过封装的代码:
public class PleaSure {
private String name;
private int age;
public PleaSure(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
要调用已经封装好的代码中的 getAge
方法,我们需要首先创建一个 PleaSure 类的实例,然后使用这个实例来调用 getAge
方法。具体代码如下:
public class Main {
public static void main(String[] args) {
// 创建一个Student对象,并通过构造函数初始化它的name和age属性
PleaSure pleasure = new PleaSure("Pleasure", 18);
// 使用student对象调用getAge方法,并打印返回的年龄
int age = pleasure.getAge();
System.out.println("age is: " + age);
}
}
那么如果现在我想要更改姓名name,那么我们需要新增加一个方法在封装的类内部:
public class Main {
public static void main(String[] args) {
// 创建一个Student对象,并通过构造函数初始化它的name和age属性
PleaSure pleasure = new PleaSure("Pleasure", 18);
// 使用student对象调用getAge方法,并打印返回的年龄
int age = pleasure.getAge();
System.out.println("age is: " + age);
pleasure.setAge(20);
age = pleasure.getAge();
System.out.println("age is: " + age);
}
}
public class PleaSure {
private String name;
private int age;
public PleaSure(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
throw new IllegalArgumentException("Age cannot be negative.");
}
}
}
此时如果我们想要更改年龄,就会得出如下结果:
当然这段代码当中,我已经通过直接在代码片段当中调用setAge来修改年龄,如果想要通过键盘输入,减少每次查找到这段setAge代码再进行修改的麻烦,我们可以通过new一个Scanner来进行键盘输入,这样就可以每次通过键盘输入,而非反复查找相关代码,节约时间。
继承
继承允许我们基于一个已存在的类(称为父类或基类)来创建一个新的类(称为子类或派生类),子类会继承父类的属性和方法,可以减少代码的重复定义。封装的作用主要可以概括为以下七点:代码重用、扩展功能、建立类之间的层次关系、多态性的基础、模板化设计、访问控制、实现接口的一种替代方式。
现在PleaSure分为两种,A和B,他们都是PleaSure的分支,但是他们都有自己的方法:
public class APleaSure extends PleaSure{
public APleaSure(String name, int age) {
super(name, age);
}
public void guanzhu(){
System.out.println("关注");
}
}
//****的PleaSure乐事水印
public class BPleaSure extends PleaSure{
public BPleaSure(String name, int age) {
super(name, age);
}
public void dianzan(){
System.out.println("点赞");
}
}
需要一定注意的是,在继承后我们必须先通过super关键字(指代父类),实现父类的构造方法。当我们已经像上面这样完成了两个继承完毕的子类,我们可以通过如下方法完成调用方法:
public class Main {
public static void main(String[] args) {
// 创建APleaSure实例
APleaSure apleaSure = new APleaSure("Alice", 30);
// 调用APleaSure的guanzhu方法
apleaSure.guanzhu();
// 创建BPleaSure实例
BPleaSure bpleaSure = new BPleaSure("Bob", 25);
// 调用BPleaSure的dianzan方法
bpleaSure.dianzan();
}
}
需要注意的是,每一个子类必须定义一个实现父类构造方法的构造方法,也就是需要在构造方法开始使用super,如果父类使用的是默认构造方法,那么子类不用手动指明。
所有类都默认继承自Object类,除非手动指定类型,但是依然改变不了最顶层的父类是Object类。所有类都包含Object类中的方法。
多态
多态允许一个引用变量在运行时指向多种实际类型的对象,并且这些对象可以执行相同的方法但表现出不同的行为。多态的作用主要包含以下几点:增加程序的扩展性和可维护性、接口和抽象类的实现、动态绑定、提高代码复用性和灵活性。
重写
方法的重写和重载是不一样的,重载是原有的方法逻辑不变的情况下,支持更多参数的实现,而重写是直接覆盖原有方法。
假设我们在父类中有这样一个方法:
public void study(){
System.out.println("点赞");
}
在子类中我们通过重写后的代码如下:
@Override
public void study(){
System.out.println("关注");
}
那么我们在主函数中调用study方法后我们会得到如下结果:
public class Main {
public static void main(String[] args) {
// 创建APleaSure实例
APleaSure apleaSure = new APleaSure("Alice", 30);
// 调用APleaSure的guanzhu方法
apleaSure.study();
}
}
当我们在重写方法时,不仅想使用我们自己的逻辑,同时还希望执行父类的逻辑(也就是调用父类的方法)时我们可以写如下代码:
public void study(){
super.study();
System.out.println("给你看点好康的");
}
同理,如果想访问父类的成员变量,也可以使用super关键字来访问,例如:
public void setTest(int test){
test = 1;
this.test = 1;
super.test = 1;
}
instanceof关键字
我们如果只是得到一个父类引用,但是不知道它到底是哪一个子类的实现时,我们可以使用instanceof关键字来帮助我们进行类型判断。
final关键字
final关键字能够使得一个变量的值不可更改,那么如果在类前面声明final,则该类无法再被继承,不允许子类的存在。如果类的成员属性被声明为final,那么必须在构造方法中或是在定义时赋初始值。
private final String name; //引用类型不允许再指向其他对象
private final int age; //基本类型值不允许发生改变
public Student(String name, int age) {
this.name = name;
this.age = age;
}
抽象类
类本身就是一种抽象,而抽象类,把类还要抽象,也就是说抽象类可以只保留特征,而不保留具体呈现形态,比如方法可以定义好,但是我可以不去实现它,而是交由子类来进行实现。例如:
public abstract class Student { //抽象类
public abstract void test(); //抽象方法
}
通过使用abstract关键字来表明一个类是一个抽象类,抽象类可以使用abstract关键字来表明一个方法为抽象方法,也可以定义普通方法,抽象方法不需要编写具体实现,但是必须由子类实现(除非子类也是一个抽象类)
但是需要注意的是,抽象类由于不是具体的类定义,因此无法直接通过new关键字来创建对象。
接口
在Java当中,接口只代表某个确切的功能,也就是只包含方法的定义,接口包含了一些列方法的具体定义,类可以实现这个接口,表示类支持接口代表的功能。使用interface实现。
public interface Eat {
void eat();
}
一个类可以实现很多个接口,类通过implements关键字来声明实现的接口,每个接口之间用逗号隔开。比如:
public class SportsStudent extends Student implements Eat, ...{
@Override
public void eat() {
}
}
总结
至此,我们大致浏览和学习了JavaSE当中有关数组、封装、继承、多态、抽象类和接口相关的知识,希望对大家有所帮助,也希望大家可以为我留下点赞、关注和收藏,这对我真的很重要,谢谢!也希望能与大家一起努力,获得更好的未来。