二十、包
包就是文件夹,用来管理各种不同功能的Java类,方便后期代码维护。
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。
使用规则: 包名.类名 //(全类名/全限定名)
例:
导包:
使用其他类的规则:
①使用同一个包中的类时,不需要导包。
②使用java.lang包中的类时,不需要导包。
③其他情况都需要导包。
④如果同时使用两个包中的同类名,需要用全类名。
例:
package test;
import com.test1.Teacher;
public class Test {
public static void main(String[] args){
Animal cat=new Cat(); //Cat类在同一个包 test 中,因此不需要导包和使用全类名,可以直接调用
Teacher teacher=new Teacher(); //使用test1中的Teacher类,因为不在同一个包中,因此需要使用导包。
//不存在不同包的同名类,不需要使用全类名。
com.test2.Teacher teacher1=new com.test2.Teacher(); //不同包中有同样的类,因此test2中需要使用全类名而不能导包
}
}
导包和全类名是两种调用其他包中类的手段。
二十一、修饰符final
规则:
如果用final修饰方法:表明该方法是最终方法,不能被重写。
如果用final修饰类:表示该类是最终类,不能被继承,不能有子类了。
如果用final修饰变量:这个量被称为常量,只能被赋值一次。
例:
Tips:
如果final修饰的变量是基本数据类型,那么变量存储的数据值不能改变。
如果final修饰的变量是引用数据类型,那么变量存储的地址值不能发生改变,对象内部的属性值可以改变。
字符串之所以不可变,就是因为原码中用final修饰。
例:
常量:
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。
常量命名规范:
单个单词:全部大写
多个单词:全部大写,单词之间用下划线隔开
二十二、权限修饰符
权限修饰符:用来控制一个成员能够被访问的范围。
可以修饰成员变量、方法、构造方法、内部类。
分类:
四种,范围从小到大: private<空着不写(缺省/默认)<protected<public
private 只能自己用
空着不写 只能本包使用
protected 受保护的 (不同包下的子类:继承可以跨包继承)
public 公共的
实际开发一般只有 public 和 private:
①成员变量一般是私有
②方法一般是公开
特例:如果方法中代码为抽取别的方法中的共性代码,这个方法一般也私有。
二十三、代码块
分类:
局部代码块、构造代码块、静态代码块
局部代码块:
省内存,括号内的变量在括号执行结束时会从内存中删除。
例:
构造代码块:
定义:写在成员位置的代码块
作用:可以把多个构造方法中重复的代码抽取出来
执行时机:创建本类对象的时候会先执行构造代码块再执行构造方法
构造代码块优先于构造方法执行
例子:
package test;
import com.test1.Teacher;
public class Test {
public static void main(String[] args){
final Teacher teacher=new Teacher();
}
}
package com.test1;
public class Teacher {
private String name;
private int age;
{
System.out.println("我要开始调用构造方法咯"); //构造代码块
}
public Teacher() {
System.out.println("空参构造");
}
}
输出结果:
静态代码块:
格式: static{ }
特点:需要通过static关键字修饰,随着类的加载而加载,只触发一次、只执行一次。
使用场景:在类加载的时候,做一些数据初始化的时候使用。
例:
package test;
import com.test1.Teacher;
public class Test {
public static void main(String[] args){
final Teacher teacher=new Teacher();
Teacher teacher1=new Teacher();
}
}
package com.test1;
public class Teacher {
private String name;
private int age;
static {
System.out.println("静态代码块执行了");
}
{
System.out.println("我要开始调用构造方法咯");
}
public Teacher() {
System.out.println("空参构造");
}
}
输出结果:
二十四、抽象类和抽象方法
抽象方法:
将共性的行为(方法)抽取到父类中后,由于每一个子类执行内容不一样,所以父类中的方法不能确定具体的方法体,该方法就可以定义为抽象方法。
tips:子类在继承父类时,若父类中存在一个抽象方法,则会强制子类对这个方法进行重写。
抽象类:
若一个类中存在抽象方法,则该类必须声明为抽象类。
抽象方法和抽象类的定义格式:
抽象方法:
public abstract 返回值类型 方法名(参数列表);
抽象类:
public abstract class 类名{ };
注意事项:
①抽象类不能示例化
②抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
③可以有构造方法。
④抽象类的子类:要么重写抽象类中的所有抽象方法,要么是抽象类。
二十五、接口
接口:接口就是一种规则,是对行为的抽象。
定义: public interface 接口名{ } //interface关键字
接口不能实例化
接口和类之间是实现关系,通过implements关键字表示:
public class 类名 implements 接口名{ }
接口的子类(实现类):要么重写接口中所有的抽象方法,要么是抽象类。
Tips:
①接口和类的实现关系既可以单实现也可以多实现:
public class 类名 implements 接口名1,接口名2 { }
②实现类还可以在继承一个类的同时实现多个接口:
public class 类名 extends 父类 implement 接口名1,接口名2 { }
示例代码:
//接口1
package test;
public interface MakeBaby {
public abstract void MakeBaby();
}
//接口2
package test;
public interface Swim {
public abstract void swim();
}
//父类
package test;
public abstract class Animal {
private String name;
public Animal(){
}
public Animal(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setAge(String name) {
this.name = name;
}
public abstract void eat();
}
//实现两个接口的子类frog
package test;
public class Frog extends Animal implements Swim,MakeBaby{
@Override
public void eat(){
System.out.println("吃虫子");
}
public Frog(){
super();
}
public Frog(String name){
super(name);
}
public void swim(){
System.out.println("青蛙在游泳");
}
public void MakeBaby(){
System.out.println("造蝌蚪");
}
}
//测试类
package test;
import com.test1.Teacher;
public class Test {
public static void main(String[] args){
Frog frog=new Frog("青蛙王子");
frog.eat(); //吃虫子
frog.swim();
frog.MakeBaby();
}
}
结果: 吃虫子
青蛙在游泳
造蝌蚪
接口中成员的特点:
成员变量:①只能是常量 ②默认修饰符:public static final
构造方法:没有构造方法
成员方法:①只能是抽象方法 ②默认修饰符:public abstract
Tips:
JDK7以前接口中只能定义抽象方法。
JDK8新特性:接口中可以定义有方法体的方法。
JDK9新特性:接口中可以定义私有方法。
接口和类之间的关系:
类和类的关系:
继承关系,只能单继承,不能多继承,但是可以多层继承。
类和接口的关系:
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
tips:
如果两个接口中存在同名方法,在这个类实现这两个接口时,只要对重名的方法重写一次。
接口和接口的关系:
继承关系,可以单继承,也可以多继承。
例:
//接口1
package test;
public interface Inter {
void method();
}
//继承接口1的接口2
package test;
public interface Inter2 extends Inter {
void method2();
}
//继承接口2的接口3
package test;
public interface Inter3 extends Inter2{
void method3();
}
//实现接口3的类
package test;
public class InterImp1 implements Inter3{
@Override
public void method() {
System.out.println("方法");
}
@Override
public void method2() {
}
@Override
public void method3() {
}
}
//需要同时重写接口1、接口2和接口3中三个方法
JDK8中接口新增的功能(允许存在有方法体的方法):
(一)默认方法
允许在接口中定义默认方法,需要用关键字default修饰:解决接口升级问题。
接口中默认方法定义格式:
格式: public default 返回值类型 方法名(参数列表){ }
范例: public default void show(){ }
Tips:
①默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写时去掉default关键字
②public可以省略,default不能省略。
③如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写。
例(不重写):
package test;
public interface Inter {
public default void show(){
System.out.println("接口默认方法");
}
void method();
}
package test;
public class InterImp2 implements Inter{
@Override
public void method() {
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
InterImp2 inter=new InterImp2();
inter.show(); //默认方法
}
}
(重写):
package test;
public interface Inter {
public default void show(){
System.out.println("接口默认方法");
}
void method();
}
package test;
public class InterImp2 implements Inter{
@Override
public void method() {
}
@Override
public void show() {
System.out.println("重写方法");
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
InterImp2 inter=new InterImp2();
inter.show(); //重写方法
}
}
(二)静态方法
允许在接口中定义静态方法,需要用static修饰
定义格式:
格式:public static 返回值类型 方法名(参数列表){ }
范例:public static void show(){ }
Tips:
①静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。
②public可以省略,static不能省略。
例:
package test;
public interface Inter {
public default void show(){
System.out.println("接口默认方法");
}
void method();
public static void look(){ //接口中定义的静态方法
System.out.println("静态方法看一看咯");
}
}
package test;
public class InterImp2 implements Inter{
@Override
public void method() {
}
//实现类中不需要对静态方法进行重写
@Override
public void show() {
System.out.println("重写方法");
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
Inter.look(); //直接用接口名调用
}
}
JDK9以后接口中新增的功能(允许定义私有方法):
定义格式:
格式1:private 返回值类型 方法名(参数列表) { }
范例1:private void show() { }
格式2: private static 返回值类型 方法名(参数列表) { }
范例2:private static void method() { }
package test;
public interface InterA {
public default void show1(){
System.out.println("SHOW1方法开始执行");
System.out.println("记录程序细节:(省略100行代码)");
}
public default void show2(){
System.out.println("SHOW2方法执行");
System.out.println("记录程序细节:(省略100行代码)");
}
}
可以看到接口中 System.out.println("记录程序细节:(省略100行代码)"); 这部分代码重复
JDK9以前采用将该部分额外设立成一个默认方法来避免重复:
package test;
public interface InterA {
public default void show1(){
System.out.println("SHOW1方法开始执行");
show();
}
public default void show2(){
System.out.println("SHOW2方法执行");
show();
}
public default void show(){
System.out.println("记录程序细节:(省略100行代码)");
}
}
这就导致了show()方法可以被外界调用,为了避免调用,JDK9允许将其设置为私有方法:
package test;
public interface InterA {
public default void show1(){
System.out.println("SHOW1方法开始执行");
show();
}
public default void show2(){
System.out.println("SHOW2方法执行");
show();
}
public static void show3(){
System.out.println("SHOW3方法执行");
show5();
}
public static void show4(){
System.out.println("SHOW4方法执行");
show5();
}
private void show(){ //为默认方法服务
System.out.println("记录程序细节:(省略100行代码)");
}
private static void show5(){ //为静态方法服务
System.out.println("记录程序细节:(省略100行代码)");
}
}
普通的私有方法是为默认方法服务的,静态的私有方法是为静态方法服务的。
总结:
①接口代表规则,是行为的抽象。想让哪一个类拥有一个行为,就让这个类实现对应的接口就可以了。
②当一个方法的参数是接口时,可以传递接口所有实现的对象,这种方式称为接口的多态。
图中 有两个类 一个是车类 一个是搬家公司类 两个类都实现了运输这一个接口,因此在调用搬家这个方法时,假设方法的参数类型设定为接口,那么使用方法时,参数既可以设定为车的对象,也可以设定为搬家公司的对象。
适配器模式
当一个接口中抽象方法过多,但是用户只需要使用其中一部分的时候,就可以适配器设计模式。
书写步骤:
编写中间类 XXXXAdapter,实现对应接口。对接口中的抽线方法进行空实现。
再让真正的实现类继承这个中间类,重写自己需要的方法。
为了避免其他类创建适配器类的对象,中间的适配器类用abstract修饰。
二十六、内部类
类的五大成员:
属性、方法、构造方法、代码块、内部类
内部类:在一个类的里面,再定义的一个类。
例:
内部类的种类:
成员内部类、静态内部类、局部内部类、匿名内部类
(一)成员内部类
写在成员位置的,属于外部类的成员。
可以被一些修饰符所修饰,如:private、默认、protected、public、static等
在成员内部类里,JDK16之前不能定义静态变量,JDK16开始可以定义静态变量。
获取成员内部类对象:
方式一: 在外部类创建方法,对外提供内部类对象
方法二:直接创建格式: 外部类名.内部类名 对象名=外部类对象.内部类对象;
例(Engine类不是被private修饰情况下):
package test;
public class Car {
String carName;
int carAge;
String carColor;
class Engine{
String engineName;
int engineAge;
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
Car car=new Car();
Car.Engine engine=new Car().new Engine();
}
}
另一种情况:
package test;
public class Car {
String carName;
int carAge;
String carColor;
private class Engine{
String engineName;
int engineAge;
}
public Engine getEngineName(){
return new Engine();
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
Car car=new Car();
System.out.println(new Car().getEngineName());
}
}
成员内部类获取外部类的变量:
package test;
public class Car {
String carName;
int carAge;
String carColor;
int a=10;
class Engine{
String engineName;
int engineAge;
int a=20;
public void show(){
int a=30;
System.out.println(a); //30
System.out.println(this.a); //20
System.out.println(Car.this.a); //10 //Car.this代表着Car类的地址
}
}
public Engine getEngineName(){
return new Engine();
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
Car car=new Car();
System.out.println(new Car().getEngineName());
Car.Engine engine=new Car().new Engine();
engine.show();
}
}
(二)静态内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
创建格式:
外部类名.内部类名 对象名=new 外部类名.内部类名();
例:(engine类是Train类的静态内部类)
Train.engine test=new Train.engine(); //创建静态内部对象
调用非静态方法的格式:先创建对象,用对象名调用。
例:(test是创建的静态内部类的对象,show1方法是静态内部类中的非静态方法)
package test;
public class Train {
static class engine{
public void show1(){
System.out.println("非静态方法show1");
}
static void show2(){
System.out.println("静态方法show2");
}
}
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
Train.engine test=new Train.engine(); //创建静态内部对象
test.show1();//调用静态内部类中的非静态方法
}
}
调用静态方法的格式:外部类名.内部类名.方法名();
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{
public static void main(String[] args){
Train.engine test=new Train.engine(); //创建静态内部对象
Train.engine.show2(); //调用静态内部类中的静态方法
}
}
(三)局部内部类
①将内部类定义在方法里面就叫做局部内部类,类似于方法里的局部变量。
②外界是无法直接使用,需要在方法内部创建对象并使用。
③该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
①例:下图中的局部内部类Inner和局部变量 a 类似
②例:
package test;
public class Train {
public void show(){
int a=10;
class Inner{
String name;
int age;
public void method1(){
System.out.println("局部内部类的非静态方法");
}
public static void method2(){
System.out.println("局部内部类的静态方法");
}
}
Inner i=new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method1();
Inner.method2(); //静态方法调用时使用类名调用
//或者使用对象名调用: i.method2();
}
}
(四)匿名内部类
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
格式:
new 类名或者接口名() { 重写方法;};
例: new Inter(){ public void show(){ } }
package test;
public interface Swim { //接口
public abstract void swim();
}
package test;
public abstract class Dog { //抽象类
public abstract void eat();
}
package test;
import com.test1.Teacher;
import java.sql.SQLOutput;
public class Test{ ///测试类
public static void main(String[] args){
new Swim(){ //匿名内部类 (接口)一个实现了Swim接口的没有名字的类
@Override
public void swim() {
System.out.println("重写了游泳的方法");
}
};
new Dog(){ //匿名内部类(类) 一个继承了狗类的没有名字的类
@Override
public void eat() {
System.out.println("狗吃骨头");
}
};
//一个作为狗类的子类的匿名内部类实现的多态
method(
new Dog() { //一个继承狗类的子类
@Override
public void eat() {
}
});
}
public static void method(Dog dog){
dog.eat();
}
}
使用场景:当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类只使用一次,就可以使用匿名内部类简化代码。