持续更新。。。
侵删
目录
一、方法
1.1 定义
修饰符 返回值类型 方法名称(参数类型 参数名称, …) {
方法体
return 返回值;
}
其中:
修饰符:现阶段的固定写法,public static
返回值类型:也就是方法最终产生的数据结果是什么类型
方法名称:方法的名字,规则和变量一样,小驼峰
参数类型:进入方法的数据是什么类型
参数名称:进入方法的数据对应的变量名称 PS:参数如果有多个,使用逗号进行分隔
方法体:方法需要做的事情,若干行代码
return:两个作用,第一停止当前方法,第二将后面的返回值还给调用处
返回值:也就是方法执行后最终产生的数据结果
//无返回值
public static void noReturnMethod() {
System.out.println("这是一个方法");
}
//有返回值
public static int returnMethod(int a, int b) {
System.out.println("方法执行啦!");
int result = a + b;
return result;
}
1.2 调用
方法名称();
方法有三种调用格式:
单独调用:方法名称(参数);
打印调用:System.out.println(方法名称(参数));
赋值调用:数据类型 变量名称 = 方法名称(参数);
注意:此前学习的方法,返回值类型固定写为void,这种方法只能够单独调用,不能进行打印调用或者赋值调用。
//该方法参上returnMethod()
// 单独调用
returnMethod(10, 20);
System.out.println("===========");
// 打印调用
System.out.returnMethod(sum(10, 20)); // 30
System.out.println("===========");
// 赋值调用
int number = returnMethod(15, 25);
number += 100;
System.out.println("变量的值:" + number); // 140
}
1.3 用处
方法其实就是若干语句的功能集合。 方法好比是一个工厂,比如,
- 蒙牛工厂:
原料:奶牛、饲料、水
产出物:奶制品- 钢铁工厂:
原料:铁矿石、煤炭
产出物:钢铁建材
其中,
参数(原料):就是进入方法的数据。
返回值(产出物):就是从方法中出来的数据。
1.4 方法重载
对于功能类似的方法来说,因为参数列表不一样,却需要记住那么多不同的方法名称,太麻烦。
方法的重载(Overload):多个方法的名称一样,但是参数列表不一样。 好处:只需要记住唯一一个方法名称 ,就可以实现类似的多个功能。
方法重载与下列因素相关:
- 参数个数不同
- 参数类型不同
- 参数的多类型顺序不同
方法重载与下列因素无关:
- 与参数的名称无关
- 与方法的返回值类型无关
public static void open(){} // 正确重载
public static void open(int a){} // 正确重载
static void open(int a,int b){} // 代码错误:和第8行冲突
public static void open(double a,int b){} // 正确重载
public static void open(int a,double b){} // 代码错误:和第6行冲突
public void open(int i,double d){} // 代码错误:和第5行冲突
public static void OPEN(){} // 代码正确不会报错,但是并不是有效重载
public static void open(int i,int j){} // 代码错误:和第3行冲突
二、类和对象
2.1 定义
类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该 类事物。
- 属性:就是该事物的状态信息。
- 行为:就是该事物能够做什么。
对象:是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性 和行为。
类和对象的关系:
- 类是对一类事物的描述,是抽象的。
- 对象是一类事物的实例,是具体的。
- 类是对象的模板,对象是类的实体。
格式:
2.1.1 类
public class ClassName {
//成员变量
//成员方法
}
其中:
定义类:就是定义类的成员,包括成员变量和成员方法。
成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
成员方法:和以前定义方法几乎是一样的。只不过把static去掉。
public class Student {
//成员变量
String name;//姓名
int age;//年龄
//成员方法
public void study() { //学习的方法
System.out.println("好好学习,天天向上");
}
public void eat() { //吃饭的方法
System.out.println("学习饿了要吃饭");
}
2.1.2 对象
类名 对象名 = new 类名();//创建对象
对象名.成员变量; //访问成员变量
对象名.成员方法();//访问成员方法
public class Test_Student {
public static void main(String[] args) {
//创建对象格式:类名 对象名 = new 类名();
Student s = new Student();
System.out.println("s:"+s);//cn.itcast.Student@100363
//直接输出成员变量值
System.out.println("姓名:"+s.name); //null
System.out.println("年龄:"+s.age); //0
System.out.println("‐‐‐‐‐‐‐‐‐‐");
//给成员变量赋值
s.name = "王富贵";
s.age = 18;
//再次输出成员变量的值
System.out.println("姓名:"+s.name); //王富贵
System.out.println("年龄:"+s.age); //18
System.out.println("‐‐‐‐‐‐‐‐‐‐");
//调用成员方法
s.study(); // "好好学习,天天向上"
s.eat(); // "学习饿了要吃饭"
}
}
- 定义的是类,new出来的是对象
2.2 成员变量与局部变量的区别
类中的位置 | 作用范围 | 初始化值 | 内存中的位置 | 生命周期 | |
---|---|---|---|---|---|
成员变量 | 类中,方法外 | 类中 | 有默认值 | 堆内存 | 随着对象的创建而存在,随着对象的消失而消失 |
局部变量 | 方法中或者方法声明上(形式参数) | 方法中 | 没有默认值。必须先定义,赋值,最后使用 | 栈内存 | 随着方法的调用而存在,随着方法的调用完毕而消失 |
2.3 封装
2.3.1 为什么要使用封装?
比如,
问题描述:定义Student的年龄时,无法阻止不合理的数值被设置进来。
解决方案:用private关键字将需要保护的成员变量进行修饰。
一旦使用了private进行修饰,那么本类当中仍然可以随意访问。但是!超出了本类范围之外就不能再直接访问了。
间接访问private成员变量,就是定义一对儿Getter/Setter方法,必须叫setXxx或者是getXxx命名规则,不能改,谁改谁报错。
- 对于Getter来说,不能有参数,返回值类型和成员变量对应;
- 对于Setter来说,不能有返回值,参数类型和成员变量对应。
- 对于基本类型当中的boolean值,Getter方法一定要写成isXxx的形式,而setXxx规则不变。
2.3.2 定义
private 数据类型 变量名 ;
public class Student {
private String name;
private int age;
}
public void setXXX(参数类型 参数) {}
public int getXXX() { return XXX;}
public class Person {
String name; // 姓名
private int age; // 年龄
public void show() {
System.out.println("我叫:" + name + ",年龄:" + age);
}
// 这个成员方法,专门用于向age设置数据
public void setAge(int num) {
if (num < 100 && num >= 9) { // 如果是合理情况
age = num;
} else {
System.out.println("数据不合理!");
}
}
// 这个成员方法,专门私语获取age的数据
public int getAge() {
return age;
}
}
- IDEA快捷键:Alt+Insert
2.4 this关键字
2.4.1 为什么用this?
当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。
如果需要访问本类当中的成员变量,需要this关键字:
2.4.2 定义:
this.成员变量名
public class Person {
String name; // 我自己的名字
// 参数name是对方的名字
// 成员变量name是自己的名字
public void sayHello(String name) {
System.out.println(name + ",你好。我是" + this.name);//this.name:我自己的名字(成员变量);name:别人的名字(参数、局部变量)
}
}
2.5 构造方法
2.5.1 为什么有构造方法?
构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。
无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法, 一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效
2.5.2 定义
public 类名称(参数类型 参数名称) {方法体}
2.5.3 注意:
- 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样
- 构造方法不要写返回值类型,连void都不写
- 构造方法不能return一个具体的返回值
- 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。
public Student() {} - 一旦编写了至少一个构造方法,那么编译器将不再赠送。
- 构造方法也是可以进行重载的。
重载:方法名称相同,参数列表不同。
public class Student {
// 成员变量
private String name;
private int age;
// 无参数的构造方法
public Student() {
System.out.println("无参构造方法执行啦!");
}
// 全参数的构造方法
public Student(String name, int age) {
System.out.println("全参构造方法执行啦!");
this.name = name;
this.age = age;
}
// Getter Setter
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
public static void main(String[] args) {
Student stu1 = new Student(); // 无参构造
System.out.println("============");
Student stu2 = new Student("王富贵", 20); // 全参构造
System.out.println("姓名:" + stu2.getName() + ",年龄:" + stu2.getAge());
// 如果需要改变对象当中的成员变量数据内容,仍然还需要使用setXxx方法
stu2.setAge(21); // 改变年龄
System.out.println("姓名:" + stu2.getName() + ",年龄:" + stu2.getAge());
}
- IDEA快捷键:Alt+Insert
2.6 标准代码----JavaBean
一个标准的类通常要拥有下面四个组成部分:
- 所有的成员变量都要使用private关键字修饰
- 为每一个成员变量编写一对儿Getter/Setter方法
- 编写一个无参数的构造方法
- 编写一个全参数的构造方法
这样标准的类也叫做Java Bean
2.7继承
2.7.1 什么是继承?
就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接 访问父类中的非私有的属性和行为。继承提高了代码的复用性。
例如父类是员工,子类是讲师,那么“讲师就是一个员工”。关系:is-a。
父类:我的就是我的。
子类:你的就是我的,我的还是我的。
2.7.2 定义
定义父类的格式:(一个普通的类定义)
public class 父类名称 {// …}
定义子类的格式:
public class 子类名称 extends 父类名称 {// …}
// 定义一个父类:员工
public class Employee {
public void method() {
System.out.println("方法执行!");
}
}
// 定义员工的一个子类:讲师
public class Teacher extends Employee {
}
//调用
public class Demo01Extends {
public static void main(String[] args) {
// 创建了一个子类对象
Teacher teacher = new Teacher();
// Teacher类当中虽然什么都没写,但是会继承来自父类的method方法。
teacher.method();
}
}
7.2.3 继承后的特点
1、成员变量----重名
(1)重名时的访问方式
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
- 直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找。
- 间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找。
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu() {
// 使用的是本类当中的,不会向下找子类的
System.out.println(num);
}
}
public class Zi extends Fu {
int numZi = 20;
int num = 200;
public void methodZi() {
// 因为本类当中有num,所以这里用的是本类的num
System.out.println(num);
}
}
public static void main(String[] args) {
Fu fu = new Fu(); // 创建父类对象
System.out.println(fu.numFu); // 只能使用父类的东西,没有任何子类内容
System.out.println("===========");
Zi zi = new Zi();
System.out.println(zi.numFu); // 10
System.out.println(zi.numZi); // 20
System.out.println("===========");
// 等号左边是谁,就优先用谁
System.out.println(zi.num); // 优先子类,200
// System.out.println(zi.abc); // 到处都没有,编译报错!
System.out.println("===========");
// 这个方法是子类的,优先用子类的,没有再向上找
zi.methodZi(); // 200
// 这个方法是在父类当中定义的,
zi.methodFu(); // 100
}
}
(2)解决方法
1、改名:从根本上解决问题
2、super
定义
super.父类成员变量名
public class Fu {
int num = 10;
}
public class Zi extends Fu {
int num = 20;
public void method() {
int num = 30;
System.out.println(num); // 30,局部变量
System.out.println(this.num); // 20,本类的成员变量
System.out.println(super.num); // 10,父类的成员变量
}
}
public class Demo01ExtendsField {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
(3)super vs. this
super | this |
---|---|
在子类的成员方法中,访问父类的成员变量。 | 在本类的成员方法中,访问本类的成员变量。 |
在子类的成员方法中,访问父类的成员方法。 | 在本类的成员方法中,访问本类的另一个成员方法。 |
在子类的构造方法中,访问父类的构造方法。 | 在本类的构造方法中,访问本类的另一个构造方法。 |
在this第三种用法当中要注意:
- this(…)调用也必须是构造方法的第一个语句,唯一一个。
- super和this两种构造调用,不能同时使用。
super:
public class Zi extends Fu {
int num = 20;
public Zi() {//构造方法
super();
}
public void methodZi() {
System.out.println(super.num); // 父类中的num
}
public void method() {
super.method(); // 访问父类中的method
System.out.println("子类方法");
}
}
this:
public class Zi extends Fu {
int num = 20;
public Zi() {
// super(); // 这一行不再赠送
this(123); // 3:本类的无参构造,调用本类的有参构造
// this(1, 2); // 错误写法!
}
public Zi(int n) {//有参构造
this(1, 2);
}
public Zi(int n, int m) {//有参构造的重载
}
public void showNum() {
int num = 10;
System.out.println(num); // 局部变量
System.out.println(this.num); // 1:本类中的成员变量
System.out.println(super.num); // 父类中的成员变量
}
public void methodA() {
System.out.println("AAA");
}
public void methodB() {
this.methodA();// 2:本类中的成员方法
System.out.println("BBB");
}
}
2、成员方法----重写
(1)什么是重写?
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
(2)重写 vs. 重载
重写 | 重载 | |
---|---|---|
方法名 | 一样 | 一样 |
参数列表 | 一样 | 不一样 |
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。
public class Fu {
public void methodFu() {
System.out.println("父类方法执行!");
}
public void method() {
System.out.println("父类重名方法执行!");
}
}
public class Zi extends Fu {
public void methodZi() {
System.out.println("子类方法执行!");
}
public void method() {
System.out.println("子类重名方法执行!");
}
}
public class Demo01ExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu();
zi.methodZi();
// 创建的是new了子类对象,所以优先用子类方法
zi.method();
}
}
(3)覆盖重写的注意事项
-
必须保证父子类之间方法的名称相同,参数列表也相同。
@Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。不写也不会报错。 -
子类方法的返回值必须【小于等于】父类方法的返回值范围。
小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。 -
子类方法的权限必须【大于等于】父类方法的权限修饰符。
小扩展提示:public > protected > (default) > private
备注:(default)不是关键字default,而是什么都不写,留空。
public class Fu {
public String method() {
return null;
}
}
public class Zi extends Fu {
@Override
public String method() {
return null;
}
}
(4)重写的作用
吸收父类的功能,自己再加上新的功能。
如手机的更新升级。
// 本来的老款手机
public class Phone {
public void call() {
System.out.println("打电话");
}
public void send() {
System.out.println("发短信");
}
public void show() {
System.out.println("显示号码");
}
}
// 定义一个新手机,使用老手机作为父类
public class NewPhone extends Phone {
@Override
public void show() {
super.show(); // 把父类的show方法拿过来重复利用
// 自己子类再来添加更多内容
System.out.println("显示姓名");
System.out.println("显示头像");
}
}
public class Demo01Phone {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.send();
phone.show();
System.out.println("==========");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.send();
newPhone.show();//有了旧手机的旧功能还有新手机的新功能
}
}
3、构造方法
继承关系中,父子类构造方法的访问特点:
- 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
- 子类构造可以通过super关键字来调用父类重载构造。
- super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
总结:
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。
public class Fu {
public Fu() {
System.out.println("父类无参构造");
}
public Fu(int num) {
System.out.println("父类有参构造!");
}
}
public class Zi extends Fu {
public Zi() {
super(); // 在调用父类无参构造方法
// super(20); // 在调用父类有参的构造方法
System.out.println("子类构造方法!");
}
public void method() {
// super(); // 错误写法!只有子类构造方法,才能调用父类构造方法。
}
}
2.8 抽象类和抽象方法
2.8.1 什么是抽象类和抽象方法
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
2.8.2 为什么会出现抽象类和抽象方法
比如说,
有一个父类:“动物”,这个父类里面有一个方法:“吃饭”。
这个父类有两个子类:“猫猫”、“修勾”,现在这两个子类要继承“动物”父类中的方法“吃饭”。那么问题来了,“猫猫”和“修勾”在吃饭问题上并不统一:“猫猫”吃鱼,“修勾”吃肉,虽然总体上看他们是在吃饭,但是具体的说他们吃的并不相同。“吃饭”这个方法太过于抽象。以此有了抽象类和抽象方法。
2.8.3 如何使用抽象类抽象方法
- 不能直接创建new抽象类对象,要创建子类对象进行使用。
- 必须用一个子类来继承抽象父类。
- 子类必须覆盖重写抽象父类当中所有的抽象方法。覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
修饰符 abstract 返回值类型 方法名 (参数列表);
public abstract class Animal {
// 这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
public abstract void eat();
// 这是普通的成员方法
// public void normalMethod() {
//
// }
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class DemoMain {
public static void main(String[] args) {
// Animal animal = new Animal(); // 错误写法!不能直接创建抽象类对象
Cat cat = new Cat();
cat.eat();
}
}
2.8.4 注意事项
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。 - 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。 - 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设 计。 - 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有 意义
2.9 接口
2.9.1 什么是接口
接口就是多个类的公共规范。
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
2.9.2 定义
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
2.9.3 接口的使用
- 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
public class 实现类名称 implements 接口名称 {
// … }
- 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
- IDEA快捷键:Alt+回车
实现:去掉abstract关键字,加上方法体大括号。
- 创建实现类的对象,进行使用。
1、含有抽象方法
(1)定义
public abstract 返回值类型 方法名称(参数列表);
(2)注意
- 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
- 这两个关键字修饰符,可以选择性地省略。(今天刚学,所以不推荐。)
- 方法的三要素,可以随意定义。
public interface MyInterfaceAbstract {
// 这是一个抽象方法
public abstract void methodAbs1();
// 这也是抽象方法
abstract void methodAbs2();
// 这也是抽象方法
public void methodAbs3();
// 这也是抽象方法
void methodAbs4();
}
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {
@Override
public void methodAbs1() {
System.out.println("这是第一个方法!");
}
@Override
public void methodAbs2() {
System.out.println("这是第二个方法!");
}
@Override
public void methodAbs3() {
System.out.println("这是第三个方法!");
}
@Override
public void methodAbs4() {
System.out.println("这是第四个方法!");
}
}
2、含有默认方法
(1)定义
接口的默认方法,可以通过接口实现类对象,直接调用;也可以被接口实现类进行覆盖重写。
public default 返回值类型 方法名称(参数列表) {
方法体
}
public interface MyInterfaceDefault {
// 抽象方法
public abstract void methodAbs();
// 新添加了一个抽象方法
// public abstract void methodAbs2();
// 新添加的方法,改成默认方法
public default void methodDefault() {
System.out.println("这是新添加的默认方法");
}
}
public class MyInterfaceDefaultA implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("实现了抽象方法,AAA");
}
}
public class MyInterfaceDefaultB implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("实现了抽象方法,BBB");
}
@Override
public void methodDefault() {
System.out.println("实现类B覆盖重写了接口的默认方法");
}
}
public class Demo02Interface {
public static void main(String[] args) {
// 创建了实现类对象
MyInterfaceDefaultA a = new MyInterfaceDefaultA();
a.methodAbs(); // 调用抽象方法,实际运行的是右侧实现类。
// 调用默认方法,如果实现类当中没有,会向上找接口
a.methodDefault(); // 这是新添加的默认方法
System.out.println("==========");
MyInterfaceDefaultB b = new MyInterfaceDefaultB();
b.methodAbs();
b.methodDefault(); // 实现类B覆盖重写了接口的默认方法
}
}
(2)默认方法的作用
接口当中的默认方法,可以解决接口升级的问题:如果对端口进行升级(加入新的方法),此时其他的执行类也需要对这个方法进行重写,太麻烦。有了默认方法之后就不用必须再进行重写了,可以写,也可以不写。
3、含有静态方法
(1)定义
public static 返回值类型 方法名称(参数列表) {
方法体
}
public interface MyInterfaceStatic {
public static void methodStatic() {
System.out.println("这是接口的静态方法!");
}
}
public class MyInterfaceStaticImpl implements MyInterfaceStatic {
//没有抽象方法,就没有必要进行覆盖重写
}
public class Demo03Interface {
public static void main(String[] args) {
// 创建了实现类对象
MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();
// 错误写法!
// impl.methodStatic();
// 直接通过接口名称调用静态方法
MyInterfaceStatic.methodStatic();
}
}
(2)注意
不能通过接口实现类的对象来调用接口当中的静态方法。
(3)使用
如果又一些东西,他们跟对象与对象之间没有关系,是同类对象所共享的内容,这样就与类相关
比如,学生小明运行一个方法,学生小红也运行这个方法。这俩方法一点区别也没有,不跟小明、小红这种对象有关系,只跟学生这个类有关系
4、含有私有方法
(1)普通私有方法
- 定义
private 返回值类型 方法名称(参数列表) {
方法体
}
public interface MyInterfacePrivateA {
public default void methodDefault1() {
System.out.println("默认方法1");
methodCommon();
}
public default void methodDefault2() {
System.out.println("默认方法2");
methodCommon();
}
private void methodCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
- 作用
为了解决两个默认方法之间重复代码的问题,我们需要抽取一个共有方法。
但是这个共有方法不应该让实现类使用,应该是私有化的。
(2)静态私有方法
- 定义
private static 返回值类型 方法名称(参数列表) {
方法体
}
public interface MyInterfacePrivateB {
public static void methodStatic1() {
System.out.println("静态方法1");
methodStaticCommon();
}
public static void methodStatic2() {
System.out.println("静态方法2");
methodStaticCommon();
}
private static void methodStaticCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
- 作用
解决多个静态方法之间重复代码问题
5、常量
(1)定义
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
public static final 数据类型 常量名称 = 数据值;
一旦使用final关键字进行修饰,说明不可改变。
public interface MyInterfaceConst {
// 这其实就是一个常量,一旦赋值,不可以修改
public static final int NUM_OF_MY_CLASS = 12;
}
(2)注意
- 接口当中的常量,可以省略public static final,注意:不写也照样是这样。
- 接口当中的常量必须进行赋值;不能不赋值。
- 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
2.9.4 接口的多实现
1、什么是接口的多实现
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
2、定义
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {
// 覆盖重写所有抽象方法
}
注意:1. 接口是没有静态代码块或者构造方法的。
2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
5. 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
6. 一个类如果其直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。
public interface MyInterfaceA {
// 错误写法!接口不能有静态代码块
// static {
//
// }
// 错误写法!接口不能有构造方法
// public MyInterfaceA() {
//
// }
public abstract void methodA();
public abstract void methodAbs();//重复
public default void methodDefault() {
System.out.println("默认方法AAA");
}
}
public interface MyInterfaceB {
public abstract void methodB();
public abstract void methodAbs();//重复
public default void methodDefault() {
System.out.println("默认方法BBB");
}
}
public class MyInterfaceImpl /*extends Object*/ implements MyInterfaceA, MyInterfaceB {
@Override
public void methodA() {
System.out.println("覆盖重写了A方法");
}
@Override
public void methodB() {
System.out.println("覆盖重写了B方法");
}
@Override
public void methodAbs() {
System.out.println("覆盖重写了AB接口都有的抽象方法");
}
@Override
public void methodDefault() {
System.out.println("对多个接口当中冲突的默认方法进行了覆盖重写");
}
}
public abstract class MyInterfaceAbstract implements MyInterfaceA, MyInterfaceB {//如果没有重写methodB()就得改为抽象类
@Override
public void methodA() {
}
@Override
public void methodAbs() {//重复的抽象方法,只用重写一次
}
@Override
public void methodDefault() {
}
}
public class Fu {
public void method() {
System.out.println("父类方法");
}
}
public class Zi extends Fu implements MyInterface {
}
public interface MyInterface {
public default void method() {
System.out.println("接口的默认方法");
}
}
public class Demo01Interface {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
2.9.5 接口的多继承
一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。
public interface MyInterfaceA {
public abstract void methodA();
public abstract void methodCommon();
public default void methodDefault() {
System.out.println("AAA");
}
}
public interface MyInterfaceB {
public abstract void methodB();
public abstract void methodCommon();
public default void methodDefault() {
System.out.println("BBB");
}
}
public interface MyInterface extends MyInterfaceA, MyInterfaceB {
public abstract void method();
@Override
public default void methodDefault() {
}
}
public class MyInterfaceImpl implements MyInterface {
@Override
public void method() {
}
@Override
public void methodA() {
}
@Override
public void methodB() {
}
@Override
public void methodCommon() {
}
}
2.10 多态
2.10.1 什么是多态
如:
小明是学生,同时也是一个人类
小明作为一个对象,既有学生形态,也有人类形态。这就是对象的多态性
2.10.2 定义
父类名称 对象名 = new 子类名称();(左父右子就是多态)
或者:
接口名称 对象名 = new 实现类名称();
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。
public class Fu {
public void method() {
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有方法");
}
}
public class Zi extends Fu {
@Override
public void method() {
System.out.println("子类方法");
}
}
public class Demo01Multi {
public static void main(String[] args) {
// 使用多态的写法
// 左侧父类的引用,指向了右侧子类的对象
Fu obj = new Zi();
obj.method();
obj.methodFu();
}
}
2.10.3 多态中成员变量的使用特点
1、访问成员变量的两种方式
- 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
- 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
public class Fu /*extends Object*/ {
int num = 10;
public void showNum() {
System.out.println(num);
}
public void method() {
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有方法");
}
}
public class Zi extends Fu {
int num = 20;
int age = 16;
@Override
public void showNum() {
System.out.println(num);
}
@Override
public void method() {
System.out.println("子类方法");
}
public void methodZi() {
System.out.println("子类特有方法");
}
}
public class Demo01MultiField {
public static void main(String[] args) {
// 使用多态的写法,父类引用指向子类对象
Fu obj = new Zi();
System.out.println(obj.num); // 父:10
// System.out.println(obj.age); // 错误写法!
System.out.println("=============");
// 子类没有覆盖重写,就是父:10
// 子类如果覆盖重写,就是子:20
obj.showNum();
}
}
2.10.4 多态中成员方法是使用特点
在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。
口诀:编译看左边,运行看右边。
对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
public class Demo02MultiMethod {
public static void main(String[] args) {
Fu obj = new Zi(); // 多态
obj.method(); // 父子都有,优先用子
obj.methodFu(); // 子类没有,父类有,向上找到父类
// 编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错。
// obj.methodZi(); // 错误写法!
}
}
2.10.5 使用多态的好处
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。
2.10.6 类型转换
1、向上转型
(1)定义
父类类型 变量名 = new 子类类型();
向上转型一定是安全的
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
// 子类特有方法
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
public class Demo01Main {
public static void main(String[] args) {
// 对象的向上转型,就是:父类引用指向之类对象。
Animal animal = new Cat(); // 本来创建的时候是一只猫
animal.eat(); // 猫吃鱼
// animal.catchMouse(); // 错误写法!
// 向下转型,进行“还原”动作
Cat cat = (Cat) animal;
cat.catchMouse(); // 猫抓老鼠
// 下面是错误的向下转型
// 本来new的时候是一只猫,现在非要当做狗
// 错误写法!编译不会报错,但是运行会出现异常:
// java.lang.ClassCastException,类转换异常
Dog dog = (Dog) animal;
}
}
2、向下转型
(1)定义
子类类型 变量名 = (子类类型) 父类变量名;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃SHIT");
}
public void watchHouse() {
System.out.println("狗看家");
}
}
public class Demo01Main {
public static void main(String[] args) {
// 对象的向上转型,就是:父类引用指向之类对象。
Animal animal = new Cat(); // 本来创建的时候是一只猫
animal.eat(); // 猫吃鱼
// animal.catchMouse(); // 错误写法!
// 向下转型,进行“还原”动作
Cat cat = (Cat) animal;
cat.catchMouse(); // 猫抓老鼠
// 下面是错误的向下转型
// 本来new的时候是一只猫,现在非要当做狗
// 错误写法!编译不会报错,但是运行会出现异常:
// java.lang.ClassCastException,类转换异常
Dog dog = (Dog) animal;
}
}
3、instanceof
(1)定义
对象 instanceof 类名称
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Dog(); // 本来是一只狗
animal.eat(); // 狗吃SHIT
// 如果希望掉用子类特有方法,需要向下转型
// 判断一下父类引用animal本来是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判断一下animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
giveMeAPet(new Dog());
}
public static void giveMeAPet(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
2.11 内部类
2.11.1 什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类
2.11.2 定义
类名称 对象名 = new 类名称();
public class Body { // 外部类
public class Heart { // 成员内部类
// 内部类的方法
public void beat() {
System.out.println("心脏跳动:蹦蹦蹦!");
System.out.println("我叫:" + name); // 正确写法!
}
}
// 外部类的成员变量
private String name;
// 外部类的方法
public void methodBody() {
System.out.println("外部类的方法");
new Heart().beat();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.11.3 使用
类名称 对象名 = new 类名称();
注意:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
public class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body(); // 外部类的对象
// 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
body.methodBody();
System.out.println("=====================");
// 按照公式写:
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
2.11.4 内部类的重名变量
外部类名称.this.外部类成员变量名
public class Outer {
int num = 10; // 外部类的成员变量
public class Inner /*extends Object*/ {
int num = 20; // 内部类的成员变量
public void methodInner() {
int num = 30; // 内部类方法的局部变量
System.out.println(num); // 局部变量,就近原则
System.out.println(this.num); // 内部类的成员变量
System.out.println(Outer.this.num); // 外部类的成员变量
}
}
}
public class Demo02InnerClass {
public static void main(String[] args) {
// 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
Outer.Inner obj = new Outer().new Inner();
obj.methodInner();
}
}
2.11.5 局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
1、定义
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
// …
}
}
}
class Outer {
public void methodOuter() {
class Inner { // 局部内部类
int num = 10;
public void methodInner() {
System.out.println(num); // 10
}
}
Inner inner = new Inner();
inner.methodInner();
}
}
2、使用
public class DemoMain {
public static void main(String[] args) {
Outer obj = new Outer();
obj.methodOuter();
}
}
3、final使用
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】
public class MyOuter {
public void methodOuter() {
int num = 10; // 所在方法的局部变量
class MyInner {
public void methodInner() {
System.out.println(num);
}
}
}
}
2.11.6 匿名内部类
1、什么是匿名内部类
是内部类的简化写法。它的本质是一个带具体实现的父类或者父接口的匿名的子类对象。 开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作,
- 定义子类
- 重写接口中的方法
- 创建子类对象
- 调用重写后的方法
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。
2、定义
接口名称 对象名 = new 接口名称() {
// 覆盖重写所有抽象方法
};
public class DemoMain {
public static void main(String[] args) {
// MyInterface obj = new MyInterfaceImpl();
// obj.method();
// MyInterface some = new MyInterface(); // 错误写法!
// 使用匿名内部类,但不是匿名对象,对象名称就叫objA
MyInterface objA = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-A");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-A");
}
};
objA.method1();
objA.method2();
System.out.println("=================");
// 使用了匿名内部类,而且省略了对象名称,也是匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method1();
// 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method2();
}
}
3、注意
public class DemoMain {
public static void main(String[] args) {
// MyInterface obj = new MyInterfaceImpl();
// obj.method();
// MyInterface some = new MyInterface(); // 错误写法!
// 使用匿名内部类,但不是匿名对象,对象名称就叫objA
MyInterface objA = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-A");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-A");
}
};
objA.method1();
objA.method2();
System.out.println("=================");
// 使用了匿名内部类,而且省略了对象名称,也是匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method1();
// 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method2();
}
}
三、常用类
***引用类型的使用方法
- 导包
import 包路径.类名称;
如果需要使用的目标类,和当前类位于同一个包下,则可以省略导包语句不写。
只有java.lang包下的内容不需要导包,其他的包都需要import语句。- 创建
类名称 对象名 = new 类名称();- 使用
对象名.成员方法名()
3.1 Scanner
Scanner类的功能:可以实现键盘输入数据,到程序当中。
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
public class Demo01Scanner {
public static void main(String[] args) {
// 2. 创建
// 备注:System.in代表从键盘进行输入
Scanner sc = new Scanner(System.in);
// 3. 获取键盘输入的int数字
int num = sc.nextInt();
System.out.println("输入的int数字是:" + num);
// 4. 获取键盘输入的字符串
String str = sc.next();
System.out.println("输入的字符串是:" + str);
}
}
3.2 匿名对象
只能用一次
类名称 对象名 =
public class Demo01Anonymous {
public static void main(String[] args) {
// 左边的one就是对象的名字
Person one = new Person();
one.name = "李雷";
one.showName(); // 我叫李雷
System.out.println("===============");
// 匿名对象
new Person().name = "韩梅梅";
new Person().showName(); // 我叫:null
}
}
3.3 Random
随机数
Random r = new Random();
public class Demo01Random {
public static void main(String[] args) {
Random r = new Random();
int num = r.nextInt();
System.out.println("随机数是:" + num);
}
}
3.4 ArrayList
数组的长度不可以发生改变。但是ArrayList集合的长度是可以随意变化的。对于ArrayList来说,有一个尖括号代表泛型。
泛型:也就是装在集合当中的所有元素,全都是统一的什么类型。
注意:泛型只能是引用类型,不能是基本类型。
注意事项:
**对于ArrayList集合来说,直接打印得到的不是地址值,而是内容。**如果内容是空,得到的是空的中括号:[]
public class Demo02ArrayList {
public static void main(String[] args) {
// 创建了一个ArrayList集合,集合的名称是list,里面装的全都是String字符串类型的数据
// 备注:从JDK 1.7+开始,右侧的尖括号内部可以不写内容,但是<>本身还是要写的。
ArrayList<String> list = new ArrayList<>();
System.out.println(list); // []
// 向集合当中添加一些数据,需要用到add方法。
list.add("王小明");
System.out.println(list); // [王小明]
list.add("赵小明");
list.add("钱小明");
list.add("孙小明");
System.out.println(list); // [王小明,赵小明,钱小明,孙小明]
// list.add(100); // 错误写法!因为创建的时候尖括号泛型已经说了是字符串,添加进去的元素就必须都是字符串才行
}
}
常用方法
public boolean add(E e):向集合当中添加元素,参数的类型和泛型一致。返回值代表添加是否成功。
备注:对于ArrayList集合来说,add添加动作一定是成功的,所以返回值可用可不用。
但是对于其他集合(今后学习)来说,add添加动作不一定成功。
public E get(int index):从集合当中获取元素,参数是索引编号,返回值就是对应位置的元素。
public E remove(int index):从集合当中删除元素,参数是索引编号,返回值就是被删除掉的元素。
public int size():获取集合的尺寸长度,返回值是集合中包含的元素个数。
3.5 String
3.5.1 什么是String
java.lang.String类代表字符串。
API当中说:Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。其实就是说:程序当中所有的双引号字符串,都是String类的对象。(就算没有new,也照样是。)
3.5.2 字符串的特点
- 字符串的内容永不可变。【重点】
- 正是因为字符串不可改变,所以字符串是可以共享使用的。
- 字符串效果上相当于是char[]字符数组,但是底层原理是byte[]字节数组。
3.5.3 String的构造与创建
三种构造方法:
public String():创建一个空白字符串,不含有任何内容。
public String(char[] array):根据字符数组的内容,来创建对应的字符串。
public String(byte[] array):根据字节数组的内容,来创建对应的字符串。
一种直接创建:
String str = “Hello”;
public class Demo01String {
public static void main(String[] args) {
// 使用空参构造
String str1 = new String(); // 小括号留空,说明字符串什么内容都没有。
System.out.println("第1个字符串:" + str1);
// 根据字符数组创建字符串
char[] charArray = { 'A', 'B', 'C' };
String str2 = new String(charArray);
System.out.println("第2个字符串:" + str2);
// 根据字节数组创建字符串
byte[] byteArray = { 97, 98, 99 };
String str3 = new String(byteArray);
System.out.println("第3个字符串:" + str3);
// 直接创建
String str4 = "Hello";
System.out.println("第4个字符串:" + str4);
}
}
3.5.4 字符串常量池
字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。
对于基本类型来说,==是进行数值的比较。
对于引用类型来说,==是进行地址值的比较。
public class Demo02StringPool {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
char[] charArray = {'a', 'b', 'c'};
String str3 = new String(charArray);
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // false
}
}
3.5.6 String的常用方法
1、获取
- public int length():获取字符串当中含有的字符个数,拿到字符串长度。
- public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串。
- public char charAt(int index):获取指定索引位置的单个字符。(索引从0开始。)
- public int indexOf(String str):查找参数字符串在本字符串当中首次出现的索引位置,如果没有返回-1值。
public class Demo02StringGet {
public static void main(String[] args) {
// 获取字符串的长度
int length = "asdasfeutrvauevbueyvb".length();
System.out.println("字符串的长度是:" + length);
// 拼接字符串
String str1 = "Hello";
String str2 = "World";
String str3 = str1.concat(str2);
System.out.println(str1); // Hello,原封不动
System.out.println(str2); // World,原封不动
System.out.println(str3); // HelloWorld,新的字符串
System.out.println("==============");
// 获取指定索引位置的单个字符
char ch = "Hello".charAt(1);
System.out.println("在1号索引位置的字符是:" + ch);
System.out.println("==============");
// 查找参数字符串在本来字符串当中出现的第一次索引位置
// 如果根本没有,返回-1值
String original = "HelloWorldHelloWorld";
int index = original.indexOf("llo");
System.out.println("第一次索引值是:" + index); // 2
System.out.println("HelloWorld".indexOf("abc")); // -1
}
}
2、截取
- public String substring(int index):截取从参数位置一直到字符串末尾,返回新字符串。
- public String substring(int begin, int end):截取从begin开始,一直到end结束,中间的字符串。
备注:[begin,end),包含左边,不包含右边。
public class Demo03Substring {
public static void main(String[] args) {
String str1 = "HelloWorld";
String str2 = str1.substring(5);
System.out.println(str1); // HelloWorld,原封不动
System.out.println(str2); // World,新字符串
System.out.println("================");
String str3 = str1.substring(4, 7);
System.out.println(str3); // oWo
System.out.println("================");
// 下面这种写法,字符串的内容仍然是没有改变的
// 下面有两个字符串:"Hello","Java"
// strA当中保存的是地址值。
// 本来地址值是Hello的0x666,
// 后来地址值变成了Java的0x999
String strA = "Hello";
System.out.println(strA); // Hello
strA = "Java";
System.out.println(strA); // Java
}
}
3、转换
1.public char[] toCharArray():将当前字符串拆分成为字符数组作为返回值。
2. public byte[] getBytes():获得当前字符串底层的字节数组。
3. public String replace(CharSequence oldString, CharSequence newString):将所有出现的老字符串替换成为新的字符串,返回替换之后的结果新字符串。
备注:CharSequence意思就是说可以接受字符串类型。
public class Demo04StringConvert {
public static void main(String[] args) {
// 转换成为字符数组
char[] chars = "Hello".toCharArray();
System.out.println(chars[0]); // H
System.out.println(chars.length); // 5
System.out.println("==============");
// 转换成为字节数组
byte[] bytes = "abc".getBytes();
for (int i = 0; i < bytes.length; i++) {
System.out.println(bytes[i]);
}
System.out.println("==============");
// 字符串的内容替换
String str1 = "How do you do?";
String str2 = str1.replace("o", "*");
System.out.println(str1); // How do you do?
System.out.println(str2); // H*w d* y*u d*?
System.out.println("==============");
String lang1 = "会不会玩儿呀!你大爷的!你大爷的!你大爷的!!!";
String lang2 = lang1.replace("你大爷的", "****");
System.out.println(lang2); // 会不会玩儿呀!****!****!****!!!
}
}
4、分割
public String[] split(String regex):按照参数的规则,将字符串切分成为若干部分。
public class Demo05StringSplit {
public static void main(String[] args) {
String str1 = "aaa,bbb,ccc";
String[] array1 = str1.split(",");
for (int i = 0; i < array1.length; i++) {
System.out.println(array1[i]);
}
System.out.println("===============");
String str2 = "aaa bbb ccc";
String[] array2 = str2.split(" ");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
System.out.println("===============");
String str3 = "XXX.YYY.ZZZ";
String[] array3 = str3.split("\\.");
System.out.println(array3.length); // 0
for (int i = 0; i < array3.length; i++) {
System.out.println(array3[i]);
}
}
}
3.5 Static
如果一个成员变量使用了static关键字,那么这个变量不再属于对象自己,而是属于所在的类。多个对象共享同一份数据。
public class Student {
private int id; // 学号
private String name; // 姓名
private int age; // 年龄
static String room; // 所在教室
private static int idCounter = 0; // 学号计数器,每当new了一个新对象的时候,计数器++
public Student() {
this.id = ++idCounter;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
this.id = ++idCounter;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Demo01StaticField {
public static void main(String[] args) {
Student two = new Student("王小明", 16);
two.room = "101教室";
System.out.println("姓名:" + two.getName()
+ ",年龄:" + two.getAge() + ",教室:" + two.room
+ ",学号:" + two.getId());
Student one = new Student("李小明", 19);
System.out.println("姓名:" + one.getName()
+ ",年龄:" + one.getAge() + ",教室:" + one.room
+ ",学号:" + one.getId());
}
}
3.5.1 静态方法
一旦使用static修饰成员方法,那么这就成为了静态方法。静态方法不属于对象,而是属于类的。
如果没有static关键字,那么必须首先创建对象,然后通过对象才能使用它。
如果有了static关键字,那么不需要创建对象,直接就能通过类名称来使用它。
无论是成员变量,还是成员方法。如果有了static,都推荐使用类名称进行调用。
静态变量:类名称.静态变量
静态方法:类名称.静态方法()
注意事项:
- 静态不能直接访问非静态。
原因:因为在内存当中是【先】有的静态内容,【后】有的非静态内容。
“先人不知道后人,但是后人知道先人。” - 静态方法当中不能用this。
原因:this代表当前对象,通过谁调用的方法,谁就是当前对象。
public class MyClass {
int num; // 成员变量
static int numStatic; // 静态变量
// 成员方法
public void method() {
System.out.println("这是一个成员方法。");
// 成员方法可以访问成员变量
System.out.println(num);
// 成员方法可以访问静态变量
System.out.println(numStatic);
}
// 静态方法
public static void methodStatic() {
System.out.println("这是一个静态方法。");
// 静态方法可以访问静态变量
System.out.println(numStatic);
// 静态不能直接访问非静态【重点】
// System.out.println(num); // 错误写法!
// 静态方法中不能使用this关键字。
// System.out.println(this); // 错误写法!
}
}
public class Demo02StaticMethod {
public static void main(String[] args) {
MyClass obj = new MyClass(); // 首先创建对象
// 然后才能使用没有static关键字的内容
obj.method();
// 对于静态方法来说,可以通过对象名进行调用,也可以直接通过类名称来调用。
obj.methodStatic(); // 正确,不推荐,这种写法在编译之后也会被javac翻译成为“类名称.静态方法名”
MyClass.methodStatic(); // 正确,推荐
// 对于本来当中的静态方法,可以省略类名称
myMethod();
Demo02StaticMethod.myMethod(); // 完全等效
}
public static void myMethod() {
System.out.println("自己的方法!");
}
}
3.6 Arrays
java.util.Arrays是一个与数组相关的工具类,里面提供了大量静态方法,用来实现数组常见的操作。
- public static String toString(数组):将参数数组变成字符串(按照默认格式:[元素1, 元素2,元素3…])
- public static void sort(数组):按照默认升序(从小到大)对数组的元素进行排序。
备注:
- 如果是数值,sort默认按照升序从小到大
- 如果是字符串,sort默认按照字母升序
- 如果是自定义的类型,那么这个自定义的类需要有Comparable或者Comparator接口的支持。
public class Demo01Arrays {
public static void main(String[] args) {
int[] intArray = {10, 20, 30};
// 将int[]数组按照默认格式变成字符串
String intStr = Arrays.toString(intArray);
System.out.println(intStr); // [10, 20, 30]
int[] array1 = {2, 1, 3, 10, 6};
Arrays.sort(array1);
System.out.println(Arrays.toString(array1)); // [1, 2, 3, 6, 10]
String[] array2 = {"bbb", "aaa", "ccc"};
Arrays.sort(array2);
System.out.println(Arrays.toString(array2)); // [aaa, bbb, ccc]
}
}
- public static double abs(double num):获取绝对值。有多种重载。
- public static double ceil(double num):向上取整。
- public static double floor(double num):向下取整。
- public static long round(double num):四舍五入。
- Math.PI代表近似的圆周率常量(double)。
public class Demo03Math {
public static void main(String[] args) {
// 获取绝对值
System.out.println(Math.abs(3.14)); // 3.14
System.out.println(Math.abs(0)); // 0
System.out.println(Math.abs(-2.5)); // 2.5
System.out.println("================");
// 向上取整
System.out.println(Math.ceil(3.9)); // 4.0
System.out.println(Math.ceil(3.1)); // 4.0
System.out.println(Math.ceil(3.0)); // 3.0
System.out.println("================");
// 向下取整,抹零
System.out.println(Math.floor(30.1)); // 30.0
System.out.println(Math.floor(30.9)); // 30.0
System.out.println(Math.floor(31.0)); // 31.0
System.out.println("================");
System.out.println(Math.round(20.4)); // 20
System.out.println(Math.round(10.5)); // 11
}
}