static
static修饰成员变量
1、static是什么?
- 叫静态,可以修饰成员变量,成员方法
2、成员变量按有无static修饰,分为两种
- 类变量:有static修饰,属于类,在计算机中只有一份,会被类的全部对象共享
- 实例变量(对象的变量):无static修饰,属于每个对象的。
类变量应用场景
应用场景
- 在开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来使用
案例导学:
系统启动后,要求用户类可以记住自己创建了多少个用户对象了
static修饰成员方法
成员方法分类
- 类方法:有static修饰的成员方法,属于类
- 实例方法:无static修饰的成员方法,属于对象,只能用对象调用
应用场景
- 类方法最常见的应用场景是做工具类
工具类是什么?
- 工具类的方法都是类方法,每个类方法都是一个功能,是给开发人员使用的
使用类方法有什么好处
- 提高了代码的复用性
为什么工具类中的方法用类方法,而不用实例方法?
- 不需要生成对象,可以节省内存
- 实例方法需要创建对象来调用,没有必要
工具类
- 工具类没有创建对象的需求,建议将工具类的构造器私有
static注意事项
1、类方法中可以直接访问类的成员,不可以直接访问实例成员。
2、实例方法中既可以直接访问类成员,也可以直接访问实例成员。
3、实例方法中可以出现this关键字,类方法中不可以出现this关键字。
static应用-代码块
代码块概述
- 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)
代码块分两种
1、静态代码块
- 格式:static {}
- 特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次
- 作用:完成类的初始化,例如:对类变量的初始化赋值
2、实例代码块
- 格式:{}
- 特点:每次创建对象时,执行实例代码块,并在构造器之前执行
- 作用:和构造器一样,都是来完成对象的初始化的,例如:对实例变量进行初始化赋值。
static应用-单例设计模式
什么是设计模式?
- 一个问题通常有n种解法,最优解就叫设计模式。
单例设计模式
- 确保一个类只有一个对象
public class A {
private static A a = new A();
private A(){
}
public static A getObect() {
return a;
}
}
写法
- 类的构造器私有
- 定义一个变量记住类的一个对象
- 定义一个方法,返回对象
单例模式的应用场景和好处?
- Runtime
- 任务管理器
单例模式的实现方式很多
- 饿汉式单例:获取类的对象时,对象已经创建好了
- 懒汉式单例:获取类的对象时,对象才开始创建
懒汉式单例设计模式
拿对象时,才开始创建对象
写法
- 类的构造器私有
- 定义一个变量存储类的一个对象
- 定义一个方法,返回对象
public class B {
private static B b;
private B(){
}
public static B getInstance(){
if (b == null) {
b = new B();
}
return b;
}
}
继承
继承的快速入门
什么是继承
Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立父子关系
继承的特点
子类能继承父类的非私有成员(成员变量,成员方法)
继承后对象的创建
- 子类的对象是由子类、父类共同完成的
- 对象能直接访问什么成员,是由子类父类多张设计图共同决定的,多张设计图对外暴露了什么成员,对象就可以访问什么成员
继承的好处
- 减少重复代码的编写
继承注意事项
权限修饰符
限制类中的成员能够被访问的范围
注意:
protected只能在子类中访问,在外面哪怕是子类创建的对象也不行。
单继承
Java是单继承的,一个类只能继承一个直接父类;Java不支持多继承,但是支持多层继承。
Object类
Object类是Java所有类的祖宗类。
方法重写
什么是方法重写
- 子类觉得父类中的某个方法不好用,或无法满足自己的需求时,子类可以重写一个方法名称,参数列表一样的方法,去覆盖父类的方法
- 注意:重写后,方法的访问,Java会遵循就近原则
注意事项
- 小技巧:使用Override注解,会检查重写格式是否正确,可读性也会更好。
- 子类重写父类方法时,访问权限必须大于等于父类该方法的权限。
- 重写的方法返回值类型,必须与被重写方法的一样,或者范围更小。
- 私有、静态方法不能被重写,会报错。
常见应用场景
- 子类重写Object类中的toString()方法,以便返回对象的内容。
System.out.println(b.toString());
System.out.println(b);
打印对象时,默认会调用toString。
子类访问成员变量的特点
1、就近原则
- 先在子类局部找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没找到则报错
2、如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要使用父类的怎么办?
可以通过super关键字
public void showName(){
String name = "jubu";
System.out.println(name);// 局部
System.out.println(this.name);// 自己
System.out.println(super.name);// 父类
}
子类构造器的特点
- 子类的全部构造器,全都会先调用父类的构造器,再执行自己
子类构造器是如何实现调用父类构造器的
- 默认情况下,子类全部构造器第一行代码都是super(),它会调用父类的无参构造器。
- 如果父类没有无参数构造器,则必须在子类构造器第一行手写super(...),指定去调用父类的有参数构造器。
为什么这么干?应用场景
子类构造器可以通过调用父类构造器,把对象中包含父类这部分的数据先初始化,再回来吧对象中包含子类这部分的数据蛇进行初始化赋值
this(...)调用兄弟构造器
任意类的构造器中,可以通过this(...)去调用该类的其他构造器的
多态
认识多态
什么是多态
- 多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态
多态具体代码实现
People p1 = new Teacher();
p1.run();
People p2 = new Student();
p2.run();
编译看左边,运行看右边
多态的前提
- 有继承/实现关系
- 存在父类引用子类对象
- 存在方法重写
注意事项
- 多态是对象、成员的多态,Java的属性(成员变量)不谈多态
多态的好处
- 在多态形式下,右边对象是解耦合的,更便于扩展和维护
- 定义方法时,使用父类类型的形参,可以接受一切子类对象,扩展性更强、更便利
多态的缺点
- 无法直接调用子类的独有功能
强制类型转换
注意事项
- 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错
- 运行时,如果发现对象的真实类型与强转后的不同,会报类型转换异常的错误
强转之前,建议用instanceof判断
public static void go(People p){
p.run();
if (p instanceof Student){
Student s1 = (Student) p;
s1.test();
}
}
final
- final最终的意思,可以修饰类、方法、变量
- 修饰类:最终类,不能被继承了。
- 修饰方法:不能被重写了。
- 修饰变量:有且仅能赋值一次。
final修饰变量的注意
- final修饰基本类型,变量存储的数据不能变
- final修饰引用类型,变量存储的地址不能变,但是地址指向对象的内容是可以变的
常量
- 常量:public static final,变量名全大写,用写划线分隔
- 作用:通常用于记录系统的配置信息
public static final SCHOOL_NAME = "黑马";
常量的优势
- 可读性,维护性
- 编译会,常量会被“宏替换”:出现常量的地方全部替换成记住的字面量,这样可以保证使用常量和直接用字面量性能是一样的。
抽象类
认识抽象类
抽象类、抽象方法是什么样的
- 都是用abstract修饰的;抽象方法只有方法签名,不能写方法体
抽象类的注意事项、特点
- 抽象类中不一定有抽象方法,抽象方法一定是抽象类
- 类该有的成员(成员变量、方法、构造器)抽象类都可以有
- 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
- 一个类继承抽象类,必须重写完抽象类的全部方法,否则这个类也必须定义成抽象类
抽象类的好处
- 父类知道每个子类都要做某个行为,但是每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们设计这样的抽象类,就是为了更好地支持多态。
抽象类常见应用场景-模板方法设计模式
模板方法设计模式解决了什么问题
- 解决方法中存在重复代码的问题
模板方法设计模式写法
- 定义一个抽象类
- 在里面定义2个方法
- 模板方法:相同代码放进去
- 抽象方法:具体实现交给兹拉完成
public abstract class People { public void write(){ System.out.println("title"); System.out.println(writeMain()); System.out.println("end"); } public abstract String writeMain(); }
多学一招:用final修饰模板方法
- 防止子类重写模板方法
接口
认识接口
- Java提供关键字interface
- 注意:接口不能创建对象;接口是用来被实现的,实现接口的类称为实现类
- 一个类可以实现多个接口,实现类实现多个接口,则必须重写完全部接口的全部抽象方法,否则实现类需要定义为抽象类。
接口好处
- 解决类单继承的问题,可以让类有一个亲爹的同时,还可以找多个干爹
- 通过接口去找干爹,通过implements的接口,就可以显性地知道你是谁,从而就可以放心地把你当成谁来使用了
- 一个类可以实现多个接口,一个接口也可以被多个类实现。程序可以面向接口编程,灵活地切换各种业务实现
接口新增的方法jdk8开始
public interface A {
default void test1(){
System.out.println("==默认方法==");
test2();
}
private void test2(){
System.out.println("==私有方法==");
}
static void test3(){
System.out.println("==静态方法==");
}
}
1、jdk8开始,接口中新增了哪些方法?
- 默认方法:default修饰,使用实现类的对象调用
- 静态方法:static修饰,必须用当前接口名调用
- 私有方法:private,jdk9开始才有,只能在接口内部调用
- 都默认被public修饰
2、接口中为什么要新增这些方法
- 增强接口的能力,便于项目的扩展和维护
接口的多继承
一个接口可以同时继承多个接口
注意事项
- 一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持
- 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持
- 一个类继承了父类,又同时实现了接口,父类和接口中有同名的默认方法,实现类会优先用父类的
- 一个类实现了多个接口,多个接口存在同名的默认方法,可以不冲突,这个类重写该方法即可
内部类
内部类概述
- 是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类
- 场景:一个类的内部,包含了一个完整事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类
四种形式
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类【重点】
成员内部类
public class Outer {
private int age = 99;
public static String a;
public class Inner{
private String name;
public String schoolName;
private int age = 88;
public void test(){
System.out.println(age);
System.out.println(a);
int age = 66;
System.out.println(age);
System.out.println(this.age);
System.out.println(Outer.this.age);
}
}
}
1、成员内部类是什么?如何创建其对象?
- 就是类中的一个普通成员,类似普通成员变量、成员方法
- 外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...);
2、成员内部类的实力方法中,访问其他成员有啥特点?
- 可以直接访问外部类的实例成员、静态成员
- 可以拿到当前外部类对象,格式是:外部类名.this
静态内部类
public class Outer {
public static class Inner{
private String name;
public static int a;
public void test(){
}
}
}
1、什么是静态内部类?如何创建对象?有啥特点?
- static修饰的内部类,属于外部类自己持有
-
Outer.Inner in = new Outer.Inner();
- 可以直接访问外部类的静态成员,不能直接访问外部类的实例成员
匿名内部类
- 特殊的局部内部类;匿名指的是不需要为这个类声明名字
public class Test {
public static void main(String[] args) {
Aniaml a = new Aniaml(){
@Override
public void cry() {
System.out.println("miaomiaomiao");
}
};
a.cry();
}
}
abstract class Aniaml{
public abstract void cry();
}
- 特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象
- 作用:更方便地创建一个子类对象
常见使用场景
public class Test2 {
public static void main(String[] args) {
Swimming s1 = new Swimming(){
@Override
public void swim() {
System.out.println("dog swim fast");
}
};
go(s1);
}
public static void go(Swimming s){
System.out.println("==start===");
s.swim();
}
}
interface Swimming{
void swim();
}
- 匿名内部类通常作为一个参数传输给方法
枚举类
枚举是一种特殊类
public enum A {
X,Y,Z;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Compiled from "A.java"
public final class com.itheima.d6_enum.A extends java.lang.Enum<com.itheima.d6_enum.A> {
public static final com.itheima.d6_enum.A X;
public static final com.itheima.d6_enum.A Y;
public static final com.itheima.d6_enum.A Z;
public static com.itheima.d6_enum.A[] values();
public static com.itheima.d6_enum.A valueOf(java.lang.String);
public java.lang.String getName();
public void setName(java.lang.String);
static {};
}
- 枚举类第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象
- 枚举类的构造器都是私有的,因此,枚举类不能对外创建对象
- 枚举类都是最终类,不可以被继承
- 枚举类中,从第二行开始,可以定义类的其他各种成员
- 编译器为枚举类新增了几个方法,枚举类都是继承java.lang.Enum的。
抽象枚举
package com.itheima.d6_enum;
public enum B {
X(){
@Override
public void go() {
}
},
Y("zhangsan"){
@Override
public void go() {
System.out.println(getName() + " is running~~");
}
};
public abstract void go();
private String name;
B() {
}
B(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
枚举常见应用场景
- 用来表示一组信息,然后作为参数传输
- 参数值不受约束 选择定义枚举表示一组信息,并作为参数传输
- 代码可读性好,参数值得到了约束,对使用者更友好
泛型
泛型的认识
- 定义类、接口、方法时,同时声明了一个或者多个变量(如:<E>),称为泛型类,泛型接口,泛型方法,他们统称为泛型
public class Test {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("java1");
list1.add("java2");
list1.add("java3");
for (int i = 0; i < list1.size(); i++) {
String e = list1.get(i);
System.out.println(e);
}
}
}
- 作用:泛型提供了在编译阶段约束所能操作的数据类型,并进行自动检查的能力。这样可以避免强制类型转换,及可能出现的异常。
- 泛型的本质:把具体的数据类型作为参数传给类型变量。
泛型类
public class MyArrayList <E>{
private Object[] arr = new Object[10];
private int size;
public boolean add(E e){
arr[size++] = e;
return true;
}
public E get(int index){
return (E) arr[index];
}
}
用extend限制类型
public class MyClass3 <E extends Animal>{
}
class Animal{
}
泛型接口
是用来给实现类实现的
// 泛型接口
public interface Data <T>{
void add(T t);
ArrayList<T> getByName(String name);
}
// 实现类1
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public ArrayList<Student> getByName(String name) {
return null;
}
}
// 实现类2
public class TeacherData implements Data<Teacher>{
@Override
public void add(Teacher teacher) {
}
@Override
public ArrayList<Teacher> getByName(String name) {
return null;
}
}
泛型方法
public class Test {
public static void main(String[] args) {
String rs = test("java");
System.out.println(rs);
Dog d = test(new Dog());
System.out.println(d);
ArrayList<Car> cars = new ArrayList<>();
cars.add(new BMW());
cars.add(new BENZ());
go1(cars);
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
go1(bmws);
ArrayList<BENZ> benzs= new ArrayList<>();
benzs.add(new BENZ());
go1(benzs);
}
public static <T extends Car> void go1(ArrayList<T> cars){
}
// ?通配符,使用泛型的时候代表一切类型 ? extends Car(上限) ? super Car(下限)
public static void go2(ArrayList<? extends Car> cars){
}
public static <T> T test(T t){
return t;
}
}
泛型注意事项
- 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
- 泛型不支持基本数据类型,只支持对象类型。