Java学习笔记(二)
1 static
static表示静态,是Java里面的一个修饰符,可以修饰成员变量,也可以修饰成员方法。
1.1 静态变量
- 被static修饰的成员变量叫做静态变量
- 特点:被该类所有对象共享
- 调用方式:类名调用或对象名调用
- 不属于对象,属于类
1.1.1 static内存图
注意:静态变量是随着类的加载而加载的,优先于对象出现
teacherName的初始化值为null,后被覆盖为"***"
1.2 静态方法
- 被static修饰的成员方法叫做静态方法
- 多用于测试类和工具类
- JavaBean类很少会用
- 类名调用或对象名调用
总结:
JavaBean类:用来描述一类事物的类
测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的主入口
工具类:不是用来描述一类事物,而是帮我们做一些事情的类
工具类的特点:
- 见名知意
- 私有化构造方法(即不能创建对象,因为没有意义)
- 方法定义为静态(可直接调用)
创建工具类:
package Code;
public class MathUtil {
//私有化构造方法
private MathUtil(){
}
//静态方法
public static int sum(int[] arr){
int sum=0;
for (int i = 0; i < arr.length; i++) {
sum=sum+arr[i];
}
return sum;
}
}
测试类:
package Code;
public class test51 {
public static void main(String[] args) {
int[] arr={1,2,3,4};
System.out.println(MathUtil.sum(arr));
}
}
1.2.1 练习:创建学生工具类
学生类:
package test2;
public class Student {
private String name;
private int age;
private String gender;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
工具类:
package test2;
import java.util.ArrayList;
public class StudentUtil {
private StudentUtil(){
}
public static int getMaxAge(ArrayList<Student> list){
int max=list.get(0).getAge();
for(Student i:list){
int age=i.getAge();
if(age>max){
max=age;
}
}
return max;
}
}
测试类:
package test2;
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList<Student> list=new ArrayList<>();
Student s1=new Student("cjm",22,"男");
Student s2=new Student("sqd",19,"女");
Student s3=new Student("cjm他爹",36,"女");
list.add(s1);
list.add(s2);
list.add(s3);
System.out.println(StudentUtil.getMaxAge(list));
}
}
1.2.2 总结
-
非静态方法可以访问所有
-
静态方法中没有this关键字(静态方法是共享的,无法指定对象)
-
静态方法只能访问静态变量或静态方法
- 非静态变量和非静态方法和对象有关
package test3;
public class CJM {
static String name="cjm";
private int age;
public CJM() {
}
//this表示当前方法调用者(对象)的地址值
//this由虚拟机赋值
//原来的写法:public void print1(){}
public void print1(CJM this){
System.out.println("cjm是大傻逼!");
//调用其他方法时:
//原来的写法:print2();
//实质:
this.print2(); //this——不用写
}
public void print2(CJM this){
System.out.println("cjm是大聪明!");
}
public static void print3(){
System.out.println("明猪");
}
}
1.2.3 重新认识main方法
package test3;
public class test {
public static void main(String[] args) {
//用Edit Configuration给字符串数组args传入数据cjm pig CJM PIG
System.out.println(args.length); //4
for (int i = 0; i < args.length; i++) {
System.out.print(args[i]+" "); //cjm pig CJM PIG
}
}
}
2 继承
2.1 引入
假如我们要定义如下类:
学生类,老师类和工人类,分析如下。
-
学生类
属性:姓名,年龄
行为:吃饭,睡觉 -
老师类
属性:姓名,年龄,薪水
行为:吃饭,睡觉,教书 -
班主任
属性:姓名,年龄,薪水
行为:吃饭,睡觉,管理
如果我们定义了这三个类去开发一个系统,那么这三个类中就存在大量重复的信息(属性:姓名,年龄。行为:吃饭,睡觉)。这样就导致了相同代码大量重复。
解决方法:
假如多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。
其中,多个类可以称为子类,单独被继承的那一个类称为父类、超类(superclass)或者基类。
2.2 继承的格式
通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
public class Student extends Person{}
Student称为子类,Person称为父类。
2.3 继承的应用场景
当类与类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑用继承来优化代码。
2.4 继承的特点
- 只能单继承,一个子类只能有一个直接父类
- Java不支持多继承,但支持多层继承
- Java中所有的类都直接或间接继承于Object类
子类只能访问父类中的非私有成员。
2.5 子类能继承父类中的哪些内容
父类中的内容 | 内容权限 | 能否继承 | 内容权限 | 能否继承 |
---|---|---|---|---|
构造方法 | 非私有 | 0 | private | 0 |
成员变量 | 非私有 | 1 | private | 1 |
成员方法 | 非私有 | 1 | private | 0 |
- 父类的构造方法不能被子类继承
- 父类的成员变量能被子类继承
- 父类的非私有成员方法能被子类继承
成员方法能被添加到虚方法表,就能被子类继承
Java中所有的类都直接或间接继承于Object类,Object类虚方法表长度为5,有5个虚方法
2.6 继承中各成员的访问特点
2.6.1 成员变量的访问特点
-
就近原则
- 先在局部位置(方法内部)找,本类成员量找,父类成员变量找,逐级往上
-
如果子类和父类变量出现重名
System.out.print(name);
System.out.print(this.name);
System.out.print(super.name);
2.6.2 成员方法的访问特点
- 就近原则
- 先在本类中查看被调用的方法,如果没有,就会调用从父类中继承下来的方法
super.method(); //直接调用父类的方法
2.6.2.1 方法重写
2.6.2.2 方法重写的本质
2.6.2.3 方法重写的注意事项和要求
2.6.3 构造方法的访问特点
2.7 this和super总结
2.8 练习
3 多态 包 final
3.1 多态
3.1.1 什么是多态
对象的多种形态
3.1.2 多态表现形式
父类名称 对象名称=子类名称
3.1.3 多态调用成员的特点
Animal a=new Dog();
-
调用成员变量:编译看左边,运行也看左边
- 编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个成员变量,如果有,编译成功,如果没有,编译失败
- 运行看左边:Java运行代码的时候,实际上运行的是父类的成员变量
-
调用成员方法:编译看左边,运行也看右边
-
编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个成员方法,如果有,编译成功,如果没有,编译失败
-
运行看左边:Java运行代码的时候,实际上运行的是子类的成员方法
-
实质:
成员变量:在子类的对象中,会把父类的成员变量也继承下来
成员方法:如果子类对方法进行了重写,那么子类的虚方法表会把父类的原有方法覆盖
3.1.4 多态的前提
- 有继承关系
- 有父类引用指向子类对象
- 有方法重写
3.1.5 多态的好处
使用父类进行参数传递,可以接收所有类型子类对象,体现多态的拓展性
3.1.6 多态的弊端
不能使用子类中的特有功能(方法)
解决方法:强制类型转换,将对象转换成子类类型,从而调用子类的特有功能
注意:
- 转换类型与真实对象类型不一致会报错
- 多个子类对象时,用instanceof关键字进行判断
Dog d=(Dog)a;
判断语句:
if(a instanceof Dog){
Dog d=(Dog)a;
}
JDK14以后的写法:
if(a instanceof Dog d){
}
3.2 包
3.2.1 什么是包
包就是文件夹,用来管理不同功能的Java类
3.2.2 包名规则
公司域名反写+包的作用,需要全部英文小写,见名知意
3.2.3 使用其他类的规则
- 使用同一个包中的类时,不需要导包
- 使用java.lang包中的类时,不需要导包
- 其他情况都需要导包
- 如果同时使用两个包中的同名类,需要用全类名(包名+类名)
3.3 final
- final修饰方法:表明该方法是最终方法,不能被重写
- final修饰类:表面该类是最终类,不能被继承
- final修饰的变量叫做常量,只能被赋值一次
3.3.1 常量的命名规范
- 单个单词:全部大写
- 多个单词:全部大写,单词之间用下划线"_"隔开
注意:
final修饰的是基本数据类型,变量存储的数据值不可变
final修饰的是引用数据类型,变量存储的地址值不可变,对象内部可变
4 权限修饰符和代码块
4.1 权限修饰符
4.1.1 作用范围
四种作用范围由小到大:private< 缺省/默认 < protected < public
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | ✔ | × | × | × |
缺省/默认 | ✔ | ✔ | × | × |
protected | ✔ | ✔ | ✔ | × |
public | ✔ | ✔ | ✔ | ✔ |
4.1.2 使用规则
实际开发中,一般只用private和public
- 成员变量私有
- 成员方法公开
- 如果该方法是抽取其他方法的共性代码,这个方法一般私有
4.2 代码块
4.2.1 局部代码块
提前结束变量的生命周期
package Code;
public class test51 {
public static void main(String[] args) {
{
int a=50;
}
//局部代码块
//当程序运行到这里,a会从内存中消失
System.out.println(a); //报错
}
}
4.2.2 构造代码块(不够灵活,逐渐淘汰)
- 写在成员位置的代码块
- 作用:可以把多个构造方法中重复的代码抽取出来
- 创建对象时会优先执行构造代码块再执行构造方法(每一次创建对象都会执行)
package Code;
public class test52 {
private String name;
private int age;
//构造代码块
{
System.out.println("开始创建对象");
}
public test52() {
//System.out.println("开始创建对象");
}
public test52(String name, int age) {
//System.out.println("开始创建对象");
this.name = name;
this.age = age;
}
}
解决办法:
4.2.3 静态代码块
static{}
特点:随着类的加载而加载,并且只执行一次
作用:可以实现方法的数据初始化,避免该方法被重复调用时的多次数据初始化
5 抽象类 接口 内部类
5.1 抽象类
5.1.1 抽象方法
将共性的行为(方法)抽取到父类后,由于每个子类执行的内容不一样,所以父类不能确定具体的方法体。该方法就
可以定义为抽象方法。
public abstract void work();
//强制子类必须按照这种格式重写
5.1.2 抽象类
如果一个类中存在抽象方法,该类必须声明为抽象类。
public abstract class Studnt{
}
5.1.3 注意事项
- 抽象类不能实例化(不能创建对象)
- 抽象类不一定有抽象方法,但是有抽象方法一定是抽象类
- 可以有构造方法
- 抽象类的子类:
- 要么重写抽象类中所有抽象方法
- 要么是抽象类
5.2 接口
5.2.1 接口的定义和使用
5.2.2 接口中成员的特点
注意:JDK7以前,接口中只能定义抽象方法
- 成员变量:只能是常量,**默认修饰符public static final **
- 无构造方法
- 成员方法:只能是抽象方法,默认修饰符public abstract
public interface Inter {
int a=10; //默认修饰符public static final
void work(); //默认修饰符public abstract
}
5.2.3 接口与类之间的关系
5.2.4 JDK8以后接口中的新增方法
5.2.4.1 默认方法
- 允许在接口中定义默认方法,用default关键字修饰
- 作用:解决接口升级问题
接口中默认方法定义格式:
public default void show(){
}
接口中默认方法的注意事项:
- 默认方法不是抽象方法,不强制要求被重写,如果被重写,要去掉default关键字
- public可以省略,default不可以省略
- 如果实现多个接口,多个接口中存在相同名字的默认方法,子类必须对该默认方法进行重写
5.2.4.2 静态方法
- 允许在接口中定义静态方法,用static关键字修饰
接口中静态方法定义格式:
public static void show(){
}
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过类名或对象名调用
- public可以省略,static不可以省略
5.2.4.3 JDK9以后新增的私有方法
作用:抽取接口中的重复代码,并在接口中进行调用,并且不被外界访问
- 普通的私有方法,为默认方法服务
- 静态的私有方法,为静态方法服务
接口中私有方法定义格式:
private void show1(){
}
private static void show2(){
}
5.2.5 接口总结
- 接口代表规则,是行为的抽象,想要一个类拥有一个行为,就让这个类实现对应接口
- 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称为接口多态
5.2.6 适配器设计模式
当一个接口中抽象方法太多,而实现类只需要其中一部分抽象方法,这时候就可以使用适配器设计模式。
书写步骤:
- 创建中间类XXXAdapter,实现对应接口
- 对接口中所有抽象方法进行空实现
- 让真正的实现类继承中间类,并重写需要用的方法
- 为了避免其他类创建适配器类的对象,将中间的适配器类用abstract关键字修饰
5.2.6 综合案例
5.3 内部类
类的五个成员:属性、方法、构造方法、代码块、内部类
5.3.1 什么是内部类
在一个类(外部类)的内部再创建一个类,这个类就称为内部类。
- 内部类表示的事物是外部类的一部分
- 内部类单独出现没有任何意义
5.3.2 内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
5.3.3 内部类的分类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
5.3.3.1 成员内部类
-
写在成员位置的,属于外部类的成员
-
成员内部类可以被一些修饰符修饰,比如:private、默认、protected、public
- private修饰:只能在外部类里面使用,外界不可用
- 默认权限:只能在本包使用,其他包用不了
- protected修饰:本包的其他类使用,包括其他包的子类
- public修饰:表示所有地方都可以创建成员内部类的对象
-
在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才能定义静态变量
5.3.3.2 获取成员内部类的对象
- 在外部类中编写方法,对外提供内部类的对象(private)
public class Out {
String name;
private class In{
}
public In getIn(){
return new In();
}
}
public class test {
public static void main(String[] args) {
Out o=new Out();
Object oi=o.getIn(); //获取内部类对象
}
}
- 直接创建格式:
外部类名.内部类名 对象名=外部类对象.内部类对象;
public class Out {
String name;
//内部类
protected class In{
}
}
public class test {
public static void main(String[] args) {
//创建内部类对象
Out.In oi=new Out().new In();
}
}
5.3.3.3 成员内部类的变量
- 外部类成员变量与内部类成员变量重名:Out.this获取外部类对象的地址值
public class Out {
int a=30;
public class In{
int a=20;
public void show(){
int a=10;
System.out.println(a); //10
System.out.println(this.a); //20
System.out.println(Out.this.a); //30
}
}
}
public class test {
public static void main(String[] args) {
Out.In oi=new Out().new In();
oi.show();
//或者
Out o=new Out();
o.getIn().show();
}
}
5.3.3.4 静态内部类
静态内部类只能调用外部类的静态变量和静态方法,如果在静态内部类想要访问外部类的非静态成员,需要创建外
部类对象。
5.3.3.5 创建静态内部类对象的格式
外部类名.内部类名 对象名=new 外部类名.内部类名();
-
调用静态内部类非静态方法:先创建静态内部类对象,再用对象调用
-
调用静态内部类静态方法:外部类名.内部类名.方法名();
public class Out {
int a=10;
static int b=20;
public static class In{
public void show1(){
//调用外部类静态成员变量
System.out.println(b);
//调用外部类非静态成员变量
Out o=new Out();
System.out.println(o.a);
}
public static void show2(){
//调用外部类静态成员变量
System.out.println(b);
//调用外部类非静态成员变量
Out o=new Out();
System.out.println(o.a);
}
}
}
public class test {
public static void main(String[] args) {
//调用静态内部类静态方法
Out.In.show2(); //20 10
//调用静态内部类非静态方法
Out.In oi=new Out.In();
oi.show1(); //20 10
}
}
5.3.3.6 局部内部类
- 将内部类定义在方法里面就叫局部内部类,类似方法里面的局部变量,不能用public、static、protected修饰
- 只能用final修饰
- 局部内部类不能被外界访问,只能在方法内创建局部内部类对象并使用
- 局部内部类可以直接访问外部类的成员,也可访问方法内的局部变量
public class Out {
int age=19;
public void show(){
int height=180;
class In{
public void method1(){
System.out.println("method1");
}
public static void method2(){
System.out.println("method2");
}
}
In.method2();
In i=new In();
i.method1();
}
}
public class test {
public static void main(String[] args) {
Out o=new Out();
o.show();
}
}
5.3.4 匿名内部类(重点)
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
整体是一个类的子类对象或接口的实现类对象
new 接口名/类名(){
重写方法
};
5.3.4.1 实现接口
public interface Swim {
public abstract void swim();
}
public class test {
public static void main(String[] args) {
new Swim(){
@Override
public void swim(){
System.out.println("重写抽象方法");
}
};
//实现Swim接口
//方法重写
//new创建没有名字的对象
//()表示创建方法为空参构造
}
}
5.3.4.2 继承类
public abstract class Animal {
public abstract void eat();
}
public class test {
public static void main(String[] args) {
new Animal(){
@Override
public void eat(){
System.out.println("重写抽象类里面的抽象方法");
}
};
//继承Anima父类
//方法重写
//new创建没有名字的子类对象
//()表示创建方法为空参构造
}
}
5.3.4.3 应用场景
当方法的参数是接口或类时:
子类或实现类只需要用一次,就不用那么麻烦再创建子类或实现类,直接将匿名内部类作为参数传递给方法。
public class test {
public static void main(String[] args) {
method(
new Animal(){
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
);
}
public static void method(Animal a){ //Animal a=子类对象 多态
a.eat(); //编译看左边,运行看右边
}
}
5.3.4.4 补充
public class test {
public static void main(String[] args) {
//多态
Animal a=new Animal(){
@Override
public void eat(){
System.out.println("狗吃骨头");
}
};
//接口多态
Swim s=new Swim() {
@Override
public void swim() {
System.out.println("狗刨");
}
};
new Swim() {
@Override
public void swim() {
System.out.println("狗刨");
}
}.swim(); //狗刨
}
}