Java面向对象进阶(二)
包
1.什么是包?
○ 包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护
○ 建包的语法格式:package 公司域名倒写.技术名称,报名建议全部英文小写,且具意义
○ 建包语句必须在第一行,一般IDEA工具会帮助创建
2.导包
○ 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!import包名.类名;
○ 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
以下是测试类所处包的位置
定义一个Student类(直属于d1_package包下),测试不同位置下的类调用该类,是否需要导包操作
package com.github.d1_package;
public class Student {
public void test () {
System.out.println("ok");
}
}
在相同包下的不同类Test中,可以直接访问,不需要导包
package com.github.d1_package;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.test();
}
}
在不同包下的类Test2中,必须要导包才能使用
package com.github.d1_package.itcast;
import com.github.d1_package.Student;
public class Test2 {
public static void main(String[] args) {
Student student = new Student();
student.test();
}
}
在itcast包下创建一个同名的Student类
package com.github.d1_package.itcast;
public class Student {
public void test () {
System.out.println("导包成功!");
}
}
在Test类中,导入类名相同,但所处路径不同的包时,默认只能导入一个类,另一个类要带包名访问
package com.github.d1_package;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.test();
com.github.d1_package.itcast.Student student1 = new com.github.d1_package.itcast.Student();
student1.test();
}
}
权限修饰符
1.什么是权限修饰符?
○ 权限修饰符:是用来控制一个成员变量能够被访问的范围○ 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符的成员能够被访问的范围将受到限制
2.权限修饰符的分类和具体作用范围:
○ 权限修饰符:有四种作用范围由小到大(private->缺省->protected->public)
以下是测试类所处包的位置
定义一个Fu类,用方法能否被调用来测试权限修饰符的作用范围
package com.github.d2_modifier;
public class Fu {
private void privateMethod() {
System.out.println("private");
}
void method () {
System.out.println("缺省");
}
protected void protectedMethod() {
System.out.println("protected");
}
public void publicMethod() {
System.out.println("public");
}
}
在Fu类下访问,都能成功访问
public static void main(String[] args) {
Fu fu = new Fu();
fu.privateMethod();
fu.method();
fu.protectedMethod();
fu.publicMethod();
}
在同一个包下的不同类Test中,private修饰的方法不能被调用
package com.github.d2_modifier;
public class Test {
public static void main(String[] args) {
Fu fu = new Fu();
// fu.privateMethod(); 报错
fu.method();
fu.protectedMethod();
fu.publicMethod();
}
}
在不同包下的子类Zi中,private、缺省修饰的方法不能被调用
注意:protected要求的是不同包下的子类,需要通过子类来访问
package com.github.d2_modifier.itcast;
import com.github.d2_modifier.Fu;
public class Zi extends Fu{
public static void main(String[] args) {
Fu fu = new Fu();
// fu.privateMethod(); 报错
// fu.method(); 报错
// fu.protectedMethod(); 报错
// 报错原因:protected要求的是不同包下的子类,需要通过子类来调用!
fu.publicMethod();
Zi zi = new Zi();
// zi.privateMethod(); 报错
// zi.method(); 报错
zi.protectedMethod();
zi.publicMethod();
}
}
在不同包下的其他类Test2中,private、缺省、protected修饰的方法不能被调用
package com.github.d2_modifier.itcast;
import com.github.d2_modifier.Fu;
public class Test2 {
public static void main(String[] args) {
Fu fu = new Fu();
// fu.privateMethod(); 报错
// fu.method(); 报错
// fu.protectedMethod(); 报错
fu.publicMethod();
}
}
final
1.final的作用
○ final 关键字是最终的意思,可以修饰(类、方法、变量)
○ 修饰类:表明该类是最终类,不能被继承
public final class People {
}
class Student extends People{
} // 报错:无法从final 'com.github.d3_final.People' 继承
○ 修饰方法:表明该方法是最终方法,不能被重写
public class People {
public final void speak (){
System.out.println("人会说话");
}
}
class student extends People {
@Override
public void speak() {
System.out.println("学生会说话");
// 报错:'speak()' 无法重写 'com.github.d3_final.People' 中的 'speak()';重写的方法为 final
}
}
○ 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次,相当于常量)
public class Test2 {
// 必须要进行赋值,否则会报错
public static final int onlineNumber = 125;
private final String name = "猪八戒";
public static void main(String[] args) {
// 1.修饰局部变量
final double pai = 3.14;
// pai = 5.12;
// 报错:无法将值赋给 final 变量 'pai'
// 2.修饰全局变量
// 2.1 修饰静态成员变量
// onlineNumber = 112;
// 报错:无法将值赋给 final 变量 'onlineNumber'
// 2.2 修饰实例成员变量
Test2 test2 = new Test2();
// test2.name = "天蓬元帅";
// 报错:无法将值赋给 final 变量 'name'
}
}
2.final修饰变量的注意事项
○ final修饰的变量是基本类型:那么变量存储的数据值不发生改变
○ final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生改变的
public class Teacher {
private String name;
public Teacher() {
}
public Teacher(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Test3 {
public static void main(String[] args) {
final Teacher teacher = new Teacher("张三");
// teacher = null;
// 报错:无法将值赋给 final 变量 'teacher'
System.out.println(teacher.getName()); // 张三
teacher.setName("李四");
System.out.println(teacher.getName()); // 李四
// 地址指向的对象内容发生改变
}
}
常量
1.常量
○ 常量是使用了public static final修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变
○ 常量名的命名规范;英语单词全部大写,多个单词下划线连接起来
○ 常量的作用:通常用来记录系统的配置数据
public class Test {
public static final String LOGIN_NAME = "admin";
public static final String PASSWORD = "123456";
}
2.常量做信息配置的原理、优势
○ 在编译阶段会进行“宏替换”:把使用常量的地方全部替换成真实的字面量
○ 维护系统容易,可读性更好
3.选择常量做信息标志和分类:
○ 代码可读性好,实现了软编码形式
枚举
1.枚举的概述
○ 枚举是Java中的一种特殊类型
○ 枚举的作用:“是为了做信息的标志和信息的分类”
2.定义枚举类的格式:
3.枚举的声明方式:
一、类外声明:
enum Color {
RED,GREEN,BLUE;
}
public class Test {
public static void main(String[] args) {
Color color = Color.BLUE;
System.out.println(color); // BLUE
}
}
二、内部类中声明
public class Test2 {
enum Color {
RED,GREEN,BLUE;
}
public static void main(String[] args) {
Color color = Color.BLUE;
System.out.println(color); // BLUE
}
}
4.枚举的特征:
○ 枚举类都是继承了类型:java.lang.Enum
○ 枚举都是最终类,不可以被继承
public class Test3 extends Color{ //报错: 无法从enum 'com.github.d5_enum.Color' 继承
}
○ 构造器都是私有的,枚举对外不能创建对象
public class Test3{
// Color color = new Color(); // 报错:无法实例化枚举类型
}
从枚举enum的反编码中可以看出,构造器被私有化,不能创建对象
enum Color {
RED,
GREEN,
BLUE;
private Color() {
}
}
○ 枚举类相当于是多例模式
5.枚举的内置方法
○ values() 返回枚举类中所有的值,返回类型是数组,用于迭代枚举元素
public class Test4 {
public static void main(String[] args) {
for (Color myVar : Color.values()){
// Color.values()返回一个包含所有枚举值的数组
// myVar 用于接收每一个枚举值
System.out.print(myVar + "\t"); // RED GREED BLUE
}
}
}
○ ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
public class Test5 {
public static void main(String[] args) {
Color[] arr = Color.values();
//迭代枚举
for (Color color : arr){
// 获取索引
System.out.println(color + " at index " + color.ordinal());
}
}
}
○ valueOf()方法返回指定字符串值的枚举常量
public class Test6 {
public static void main(String[] args) {
System.out.println(Color.valueOf("GREEN"));
// System.out.println(Color.valueOf("green"));
// 枚举类中不存在green会报错 报错:IllegalArgumentException: No enum constant com.github.d5_enum.Color.green
}
}
6.枚举类成员
枚举类跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所有外部无法调用。枚举既可以包含具体方法,也可以包含抽象方法,如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。
public class Test7 {
enum Season {
SPRING,SUMMER,AUTUMN,WINTER;
// 构造函数
private Season(){
System.out.println("The season is " + this.toString());
}
public void seasonInfo() {
System.out.println("Universal Season");
}
}
public static void main(String[] args) {
Season season = Season.AUTUMN;
// The season is SPRING
// The season is SUMMER
// The season is AUTUMN
// The season is WINTER
System.out.println(season);
// AUTUMN
season.seasonInfo();
// Universal Season
}
}
抽象类
1.抽象类的概述
○ 在 Java 中abstract是抽象的意思,可以修饰类、成员方法
○ abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法
2.定义抽象类、抽象方法的格式:
注意事项:
○ 抽象方法只有方法签名,不能声明方法体
○ 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错
3.抽象的使用场景
○ 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承
○ 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成,此时这个类就可以声明成抽象类
定义父类Animal并将其变成抽象类
public abstract class Animal {
public abstract void run();
}
定义子类Dog,重写抽象方法,不重写会报错
public class Dog extends Animal{
@Override
public void run() {
System.out.println("狗在跑~");
}
}```
定义测试类Test,完成测试
```java
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.run(); // 狗在跑~
}
}
注意事项:
一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
public abstract class Cat extends Animal{
// 不报错
}
4.抽象类的特征和注意事项
○ 类有的成员(成员变量、方法、构造器)抽象类都具备
○ 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
○ 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
○ 不能用abstract修饰变量、代码块、构造器
○ 最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)
public class Test2 {
Animal animal = new Animal(); // 报错: 'Animal' 为 abstract;无法实例化
}
原因;
一、抽象方法中没有方法体,创建的实例对象调用抽象方法就会报错
二、尽管抽象类中不定义抽象方法,但出于程序的严谨性,也会报错
5.抽象类的应用知识:模版方法模式
应用场景:当系统中出现同一个功能多出开发,而该功能中大部分代码是一样的,只有其中部分可能不同的使用
模版方法模式实现步骤(写作文)
○ 1.定义一个抽象类
public abstract class Write {
}
○ 2.定义2个方法,一个是模版方法:把相同代码放里面去,不同代码定义成抽象方法
public abstract class Write {
// 定义一个模版方法
public final void write() {
// 模版方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模版方法,则模版方法就失效了,因此,加上final后可以防止子类重写了模版方法,这样更安全、专业
System.out.println("请以《我最熟悉的人为题》:");
System.out.println("\t\t\t\t《我最熟悉的人》");
System.out.println("我最熟悉的人是:");
// 正文部分
System.out.println(writeMain());
System.out.println("这就是我最熟悉的人。");
}
// 定义一个抽象方法
public abstract String writeMain();
}
○ 3.子类继承抽象类,重写方法或创建对象时重写方法
public class Test3{
public static void main(String[] args) {
Write write = new Write() {
@Override
public String writeMain() {
return "他总是无微不至地照顾我……";
}
};
write.write();
/*
请以《我最熟悉的人为题》:
《我最熟悉的人》
我最熟悉的人是:
他总是无微不至地照顾我……
这就是我最熟悉的人。
*/
}
}
接口
1.接口的定义与特点
○ 接口的格式如下
○ JDK8之前接口只能是抽象方法和常量,没有其他成分
○ 接口不能实例化
○ 接口中的成员都是public修饰的,写不写都是,因为规范的目的是为了公开化
public interface InterfaceDemo {
// 1.常量
String LOGIN_NAME = "admin";
// public static final String LOGIN_NAME = "admin";
// 2.抽象方法
void run();
// public abstract void run();
}
2.接口的用法:
○ 接口是用来被类实现(implement)的,实现接口的类称为实现类。实现类可以理解成所谓的子类
○ 从上面可以看出,接口可以被类单实现,也可以被类多实现
3.接口实现的注意事项
○ 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
4.接口和接口的关系
多继承,一个接口可以同时继承多个接口
定义三个接口
public interface People {
void eat();
void speak();
}
public interface Law {
void rule();
}
接口SportMan用于继承接口People,Law,实现接口的合并
public interface SportMan extends People, Law{
void run();
}
最后实现类BasketballMan只需要连接SportMan接口就可以实现三个接口,这就是接口的多继承
public class BasketballMan implements SportMan {
@Override
public void rule() {
}
@Override
public void eat() {
}
@Override
public void speak() {
}
@Override
public void run() {
}
}
5.接口多继承的作用
○ 规范合并,整合多个接口为一个接口,便于子类实现
6.jdk8开始接口新增方法
1.第一种:默认方法
○ 类似之前写的普通实例方法,但必须用default修饰
○ 默认会public修饰,需要用接口的实现类的对象来调用
public interface Default {
default void run() {
System.out.println("跑~");
}
}
class Animal implements Default{}
class Test{
public static void main(String[] args) {
Animal animal = new Animal();
animal.run();
// Default.run(); // 报错
// Animal.run(); // 报错
}
}
2.第二种:静态方法
○ 默认会public修饰,必须static修饰
○ 注意:接口的静态方法必须用本身的接口名来调用
public interface Static {
static void run(){
System.out.println("跑~");
}
}
class Animal2 implements Static{ }
class Test2 {
public static void main(String[] args) {
Static.run();
Animal2 animal2 = new Animal2();
// animal2.run(); // 报错
// Animal.run(); // 报错
}
}
3.第三种:私有方法
○ 就是私有的实例方法,必须使用private修饰,从JDK1.9开始有
○ 只能在本类中被其他的默认方法或者私有方法访问
public interface Private {
private void run() {
System.out.println("跑~");
}
default void speak() {
// 可以在默认方法内调用
run();
look();
System.out.println("说~");
}
static void hear() {
// run(); // 报错
System.out.println("听~");
}
private void look() {
run();
System.out.println("看~");
}
}
class Animal3 implements Private{}
class Test3 {
public static void main(String[] args) {
// Private.run(); // 报错
// Animal3.run(); // 报错
Animal3 animal3 = new Animal3();
// animal3.run(); // 报错
animal3.speak(); //跑~ 跑~ 看~ 说~
}
}
接口的注意事项
○ 1.接口不能创建对象
○ 2.一个类实现多个接口,多个接口中有同样的静态方法不冲突
interface AA {
static void run(){
System.out.println("AA");
}
}
interface BB {
static void run() {
System.out.println("BB");
}
}
class CC implements AA,BB {
}
public class Test1 {
public static void main(String[] args) {
CC cc = new CC();
// cc.run(); // 报错,接口的静态方法必须用本身的接口名调用
AA.run(); // AA
BB.run(); // BB
}
}
○ 3.一个类继承了父类,同时又实现了接口,父类中和接口有同名方法,默认用父类的
interface Dog{
default void run() {
System.out.println("狗跑~");
}
}
class Animal {
public void run(){
System.out.println("动物跑~");
}
}
public class Test2 extends Animal implements Dog{
public static void main(String[] args) {
Test2 test2 = new Test2();
test2.run(); // 动物跑~
}
}
○ 4.一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可
interface Cat{
default void run(){
System.out.println("猫跑~");
}
}
interface Lion{
default void run(){
System.out.println("狮子跑~");
}
}
class Animal2 implements Cat,Lion{
@Override
public void run() {
System.out.println("动物跑~");
}
}
public class Test3 {
public static void main(String[] args) {
Animal2 animal2 = new Animal2();
animal2.run(); // 动物跑~
}
}
○ 5.一个接口继承多个接口,是没问题的,如果多个接口中存在规范冲突则不能多继承
interface AAA {
void run();
}
interface BBB {
// int run(); // 与AAA中的run方法规则犯冲突 报错
void run();
}
interface CCC extends AAA,BBB{
}