您的位置:首页 > 娱乐 > 明星 > 管理咨询公司名称_阿拉善盟小程序开发公司_seo快速排名培训_关键词排名推广

管理咨询公司名称_阿拉善盟小程序开发公司_seo快速排名培训_关键词排名推广

2025/3/10 16:13:11 来源:https://blog.csdn.net/2303_82176667/article/details/146072552  浏览:    关键词:管理咨询公司名称_阿拉善盟小程序开发公司_seo快速排名培训_关键词排名推广
管理咨询公司名称_阿拉善盟小程序开发公司_seo快速排名培训_关键词排名推广

文章目录

  • 1.类与对象
    • 1.1类和对象的区别和联系
    • 1.2属性/成员变量/字段
    • 1.3如何创建对象
    • 1.4如何访问属性
  • 2.成员方法
    • 2.1方法的调用机制原理!
    • 2.2成员方法的定义
  • 3.成员方法传参机制
    • 3.1引用数据类型的传参机制
    • 3.2成员方法返回类型是引用类型应用实例
  • 4.方法递归调用
    • 4.1方法递归调用
    • 4.2递归重要规则
    • 4.3递归调用应用实例-汉诺塔
    • 4.4递归调用应用实例-八皇后问题
  • 5.方法重载(OverLoad)
    • 5.1基本介绍
    • 5.2重载的好处
    • 5.3注意事项和使用细节
  • 6.可变参数
    • 6.1基本概念
    • 6.2基本语法
    • 6.3注意事项和使用细节
  • 7.作用域
    • 7.1基本使用
  • 8.构造方法/构造器
    • 8.1基本介绍
    • 8.2注意事项和使用细节
  • 9.javap的使用
  • 10.对象创建的流程分析
      • 流程分析!
  • 11.this 关键字
    • 11.1深入理解this
    • 11.2this 的注意事项和使用细节
    • 11.3this 的案例
  • 12.IDEA 常用快捷键
  • 13.包
    • 13.1包的三大作用
    • 13.2包基本语法
    • 13.3包的本质分析
    • 13.4包的命名
    • 13.5常用的包
    • 13.6如何引入包
    • 13.7注意事项和使用细节
  • 14.访问修饰符
    • 14.1基本介绍
    • 14.2使用的注意事项
  • 15.面向对象编程三大特征
    • 15.1基本介绍
    • 15.2封装介绍
    • 15.3封装的理解和好处
    • 15.4封装的实现步骤(三步)
    • 15.5快速入门案例
    • 15.6将构造器和setXxx 结合
  • 16.面向对象编程-继承
    • 16.1继承的基本语法
    • 16.2继承的深入讨论/细节问题
    • 16.3继承的本质分析!
  • 17.super 关键字
    • 17.1基本介绍
    • 17.2基本语法
    • 17.3super 给编程带来的便利/细节
  • 18.面向对象编程-多态
    • 18.1多[多种]态[状态]基本介绍
    • 18.2多态的具体体现
        • 方法的多态
        • 对象的多态!
    • 18.3多态注意事项和细节讨论
        • 多态的向上转型
        • 多态向下转型
        • 属性没有重写之说
        • instanceOf 比较操作符
    • 18.4java 的动态绑定机制!!
    • 18.5多态的应用
        • 多态数组
        • 多态参数
  • 19.Object 类详解
    • 19.1equals 方法
        • ==和equals 的对比!!!!!
    • 19.2如何重写 equals 方法
    • 19.3hashCode 方法
        • 总结
    • 19.4toString 方法
  • 20.finalize 方法
    • 20.1断点调试(debug)
    • 20.2断点调试介绍
    • 20.3断点调试的快捷键
  • 21.类变量和类方法
    • 21.1类变量-提出问题
    • 21.2类变量内存布局
    • 21.3什么是类变量
    • 21.4如何定义类变量
    • 21.5如何访问类变量
    • 21.6类变量使用注意事项
    • 21.7类方法基本介绍
    • 21.8类方法的调用
    • 21.9类方法经典的使用场景
    • 21.10类方法使用注意事项和细节讨论
  • 22.理解main 方法语法
    • 22.1深入理解main 方法
  • 23.代码块
    • 23.1基本介绍
    • 23.2基本语法
    • 23.3代码块的好处和案例演示
    • 23.4代码块使用注意事项和细节讨论!!!
  • 24单例设计模式
    • 24.1什么是设计模式
    • 24.2什么是单例模式
        • 饿汉式
        • 懒汉式
        • 比较
  • 25.final 关键字
    • 25.1基本介绍
    • 25.2final 使用注意事项和细节讨论
  • 26.抽象类
    • 26.1引出
    • 26.2抽象类的介绍
    • 26.3抽象类使用的注意事项和细节讨论
  • 27.抽象类最佳实践-模板设计模式
    • 27.1基本介绍
    • 27.2模板设计模式能解决的问题
    • 27.3最佳实践
  • 28.接口
    • 28.1基本介绍
    • 28.2深入讨论
    • 28.3注意事项和细节
    • 28.4实现接口VS继承类
    • 28.5接口的多态特性
  • 29.内部类
    • 29.1基本介绍
    • 29.2基本语法
    • 29.3内部类的分类
    • 29.4局部内部类的使用
    • 29.5匿名内部类的使用!!!!!
    • 29.6匿名内部类的最佳实践
    • 29.7成员内部类的使用
    • 29.8静态内部类的使用

1.类与对象

1.1类和对象的区别和联系

  1. 类是抽象的,概念的,代表一类事物,比如人类,猫类…, 即它是数据类型.
  2. 对象是具体的,实际的,代表一个具体事物, 即是实例.
  3. 类是对象的模板,对象是类的一个个体,对应一个实例

栈中的是对象引用(对象名),实际上的对象在堆中。

// 创建Person 对象
// p1 是对象名(对象引用)
// new Person() 创建的对象空间(数据) 才是真正的对象
Person p1 = new Person();
// 对象的属性默认值,遵守数组规则:

1.2属性/成员变量/字段

从概念或叫法上看: 成员变量 = 属性 = field(字段) (即成员变量是用来表示属性的,统一叫属性)

class Car {String name;//属性, 成员变量, 字段fielddouble price;String color;String[] master;//属性可以是基本数据类型,也可以是引用类型(对象,数组)
}

属性是类的一个组成部分,一般是基本数据类型, 也可是引用类型(对象,数组)。比如前面定义猫类的 int age 就是属性

注意事项和细节说明

  1. 属性的定义语法同变量,示例:访问修饰符属性类型属性名;
    访问修饰符: 控制属性的访问范围
    有四种访问修饰符public, proctected, 默认, private ,后面我会详细介绍
  2. 属性如果不赋值,有默认值,规则和数组一致。

1.3如何创建对象

  1. 先声明再创建
    Cat cat ; //声明对象cat
    cat = new Cat(); //创建
  2. 直接创建
    Cat cat = new Cat();

1.4如何访问属性

基本语法

对象名.属性名;

cat.name ;
cat.age;
cat.color;

Person p1=new Person0;
p1.age=10;
p1.name="小明";
Person p2=p1;//把p1赋给了p2,让p2指向p1
System.out.println(p2.age);

2.成员方法

在某些情况下,我们要需要定义成员方法(简称方法)。

2.1方法的调用机制原理!

  1. 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
  2. 当方法执行完毕,或者执行到return语句时,就会返回,
  3. 返回到调用方法的地方
  4. 返回后,继续执行方法后面的代码
  5. 当main方法(栈) 执行完毕,整个程序退出

2.2成员方法的定义

访问修饰符 返回数据类型 方法名(形参列表..{//方法体语句;return 返回值;
}
// 如果方法是void,则方法体中可以没有return 语句,或者只写return;

访问修饰符(作用是控制方法使用的范围)
如果不写默认访问,[有四种: public, protected, 默认, private]

方法不能嵌套定义!

3.成员方法传参机制

基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参!

3.1引用数据类型的传参机制

引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!

栈的值是地址,改的时候修改的是对应堆中的值。

例子:

public class MethodParameter02 { //编写一个main方法public static void main(String[] args) {//测试B b = new B();// int[] arr = {1, 2, 3};// b.test100(arr);//调用方法// System.out.println(" main的 arr数组 ");// //遍历数组// for(int i = 0; i < arr.length; i++) {//     System.out.print(arr[i] + "\t");// }// System.out.println();//测试Person p = new Person();p.name = "jack";p.age = 10;b.test200(p);//测试题, 如果 test200 执行的是 p = null ,下面的结果是 10//测试题, 如果 test200 执行的是 p = new Person();..., 下面输出的是10System.out.println("main 的p.age=" + p.age);//10000 }
}
class Person {String name;int age; 
}
class B {public void test200(Person p) {//p.age = 10000; //修改对象属性//思考p = new Person();p.name = "tom";p.age = 99;//思考//p = null; }//B类中编写一个方法test100,//可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化public void test100(int[] arr) {arr[0] = 200;//修改元素//遍历数组System.out.println(" test100的 arr数组 ");for(int i = 0; i < arr.length; i++) {System.out.print(arr[i] + "\t");}System.out.println();}
}
  1. B 类中编写一个方法test100,可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化?会变化
  2. B 类中编写一个方法test200,可以接收一个Person(age,sal)对象,在方法中修改该对象属性,看看原来的对象是否变化?会变化.
    这里再对class B中的p进行修改,由于在Class B中重新new 了一个p,因此p的指针发生了改变

3.2成员方法返回类型是引用类型应用实例

通过这种方式可以编写方法复制对象。

public class MethodExercise02 { //编写一个main方法public static void main(String[] args) {Person p = new Person();p.name = "milan";p.age = 100;//创建toolsMyTools tools = new MyTools();Person p2 = tools.copyPerson(p);//到此 p 和 p2是Person对象,但是是两个独立的对象,属性相同System.out.println("p的属性 age=" + p.age  + " 名字=" + p.name);System.out.println("p2的属性 age=" + p2.age  + " 名字=" + p2.name);//这里老师提示: 可以同 对象比较看看是否为同一个对象System.out.println(p == p2);//false}
}class Person {String name;int age;
}class MyTools {//编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象。克隆对象, //注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同////编写方法的思路//1. 方法的返回类型 Person//2. 方法的名字 copyPerson//3. 方法的形参 (Person p)//4. 方法体, 创建一个新对象,并复制属性,返回即可public Person copyPerson(Person p) {//创建一个新的对象Person p2 = new Person();p2.name = p.name; //把原来对象的名字赋给p2.namep2.age = p.age; //把原来对象的年龄赋给p2.agereturn p2;}
}

4.方法递归调用

4.1方法递归调用

列举两个小案例,来帮助大家理解递归调用机制

  1. 打印问题
  2. 阶乘问题
public class Recursion01 { //编写一个main方法public static void main(String[] args) {T t1 = new T();t1.test(4);//输出什么? n=2 n=3 n=4int res = t1.factorial(5); System.out.println("5的阶乘 res =" + res);}
}class T {//分析public void test(int n) {if (n > 2) {test(n - 1);} System.out.println("n=" + n);}//factorial 阶乘public int factorial(int n) {if (n == 1) {return 1;} else {return factorial(n - 1) * n;}}
}

4.2递归重要规则

1.执行一个方法时,就创建一个新的受保护的独立空间(钱空间)

2.方法的局部变量是独立的,不会相互影响,比如n变量

3.如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引
用类型的数据。
4.递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError
5.当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就
将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

4.3递归调用应用实例-汉诺塔

public class HanoiTower { //编写一个main方法public static void main(String[] args) {Tower tower = new Tower();tower.move(64, 'A', 'B', 'C');}
}class Tower {//方法//num 表示要移动的个数, a, b, c 分别表示A塔,B 塔, C 塔public void move(int num , char a, char b ,char c) {//如果只有一个盘 num = 1if(num == 1) {System.out.println(a + "->" + c);} else {//如果有多个盘,可以看成两个 , 最下面的和上面的所有盘(num-1)//(1)先移动上面所有的盘到 b, 借助 cmove(num - 1 , a, c, b);//(2)把最下面的这个盘,移动到 cSystem.out.println(a + "->" + c);//(3)再把 b塔的所有盘,移动到c ,借助amove(num - 1, b, a, c);}}
}

4.4递归调用应用实例-八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848 年提出:在8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

  1. 第一个皇后先放第一行第一列

  2. 第二个皇后放在第二行第一列、然后判断是否OK,如果不OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适

  3. 继续第三个皇后,还是第一列、第二列…….直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确解

  4. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,全部得到.

  5. 然后回头继续第一个皇后放第二列,后面继续循环执行1,2,3.4的步骤

  6. 说明:理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8]={0,4,7,5.2, 6,1.3)//对应arr下标表示第几行,即第几个皇后,arr[i]= val , val表示第i+1个皇后,放在第i+1行的第val+1列

5.方法重载(OverLoad)

5.1基本介绍

java 中允许同一个类中,多个同名方法的存在,但要求形参列表不一致!

5.2重载的好处

  1. 减轻了起名的麻烦
  2. 减轻了记名的麻烦

5.3注意事项和使用细节

1)方法名: 必须相同

2)形参列表: 必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)

3)返回类型: 无要求

6.可变参数

6.1基本概念

java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现

6.2基本语法

访问修饰符返回类型方法名(数据类型... 形参名) {
}

看一个案例类HspMethod,方法sum。

public class VarParameter01 { //编写一个main方法public static void main(String[] args) {HspMethod m = new HspMethod();System.out.println(m.sum(1, 5, 100)); //106System.out.println(m.sum(1,19)); //20}
}class HspMethod {//可以计算 2个数的和,3个数的和 , 4. 5, 。。//可以使用方法重载// public int sum(int n1, int n2) {//2个数的和//     return n1 + n2;// }// public int sum(int n1, int n2, int n3) {//3个数的和//     return n1 + n2 + n3;// }// public int sum(int n1, int n2, int n3, int n4) {//4个数的和//     return n1 + n2 + n3 + n4;// }//.....//上面的三个方法名称相同,功能相同, 参数个数不同-> 使用可变参数优化//老韩解读//1. int... 表示接受的是可变参数,类型是int ,即可以接收多个int(0-多) //2. 使用可变参数时,可以当做数组来使用 即 nums 可以当做数组//3. 遍历 nums 求和即可public int sum(int... nums) {//System.out.println("接收的参数个数=" + nums.length);int res = 0;for(int i = 0; i < nums.length; i++) {res += nums[i];}return res;}
}

6.3注意事项和使用细节

1)可变参数的实参可以为0个或任意多个。

2)可变参数的实参可以为数组。

3)可变参数的本质就是数组。

4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后

5)一个形参列表中只能出现一个可变参数

public void f3(int... nums1, double... nums2)X错误)

7.作用域

7.1基本使用

  1. 在java编程中,主要的变量就是属性(成员变量)局部变量

  2. 我们说的局部变量一般是指在成员方法中定义的变量

  3. java中作用域的分类

    全局变量:也就是属性,作用域为整个类体 (Cat类:cry eat等方法使用属性)

    局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!

  4. 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值。

public class VarScope { //编写一个main方法public static void main(String[] args) {}
}
class Cat {//全局变量:也就是属性,作用域为整个类体 Cat类:cry eat 等方法使用属性//属性在定义时,可以直接赋值int age = 10; //指定的值是 10//全局变量(属性)可以不赋值,直接使用,因为有默认值,double weight;  //默认值是0.0public void hi() {//局部变量必须赋值后,才能使用,因为没有默认值int num = 1;String address = "北京的猫";System.out.println("num=" + num);System.out.println("address=" + address);System.out.println("weight=" + weight);//属性}public void cry() {//1. 局部变量一般是指在成员方法中定义的变量//2. n 和  name 就是局部变量//3. n 和 name的作用域在 cry方法中int n = 10;String name = "jack";System.out.println("在cry中使用属性 age=" + age);}public void eat() {System.out.println("在eat中使用属性 age=" + age);//System.out.println("在eat中使用 cry的变量 name=" + name);//错误}
}
  1. 属性和局部变量可以重名,访问时遵循就近原则。

  2. 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。

  3. 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变
    量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。

  4. 作用域范围不同

    全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
    局部变量:只能在本类中对应的方法中使用

  5. 修饰符不同

    全局变量/属性可以加修饰符
    局部变量不可以加修饰符

8.构造方法/构造器

[修饰符] 方法名(形参列表){方法体;
}
  1. 构造器的修饰符可以默认, 也可以是public protected private
  2. 构造器没有返回值
  3. 方法名和类名字必须一样
  4. 参数列表和成员方法一样的规则
  5. 构造器的调用, 由系统完成

8.1基本介绍

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:

  1. 方法名和类名相同
  2. 没有返回值
  3. 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。

8.2注意事项和使用细节

1.一个类可以定义多个不同的构造器,即构造器重载

比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄。

2.构造器名和类名要相同

3.构造器没有返回值

4.构造器是完成对象的初始化,并不是创建对象

5.在创建对象时,系统自动的调用该类的构造方法

6.如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也
叫默认构造器). 比如Dog(){}, 使用javap指令反编译看看

可以使用javap Dog.class 查看

7.一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无
参构造器,除非显式的定义一下,即:Dog(){}

9.javap的使用

javap是JDK提供的一个命令行工具,javap能对给定的class文件提供的字节代码进行反编译。 通过它,可以对照源代码和字节码,从而了解很多编译器内部的工作。

使用格式

javap <options> <classes>

常用

javap -c -v 类名
  -help  --help  -?        输出此用法消息-version                 版本信息-v  -verbose             输出附加信息-l                       输出行号和本地变量表-public                  仅显示公共类和成员-protected               显示受保护的/公共类和成员-package                 显示程序包/受保护的/公共类和成员 (默认)-p  -private             显示所有类和成员-c                       对代码进行反汇编-s                       输出内部类型签名-sysinfo                 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)-constants               显示最终常量-classpath <path>        指定查找用户类文件的位置-cp <path>               指定查找用户类文件的位置-bootclasspath <path>    覆盖引导类文件的位置

10.对象创建的流程分析

class Person{//类Personint age=90;String name;Person(String n,int a){//构造器name=n;//给属性赋值age=a;//..}
}
Person p=new Person("TIMERRING",20);

流程分析!

1.加载Person类信息(Person.class) 到方法区,只会加载一次

2.在堆中分配空间(地址)

3.完成对象初始化[3.1默认初始化 age=0 name=null 3.2显式初始化age=90,name=null,3.3构造器的初始化 age =20, name=TIMERRING]

4.在对象在堆中的地址,返回给p(p是对象名,也可以理解成是对象的引用)

11.this 关键字

public class This01 { //编写一个main方法public static void main(String[] args) {Dog dog1 = new Dog("大壮", 3);System.out.println("dog1的hashcode=" + dog1.hashCode());//dog1调用了 info()方法dog1.info(); System.out.println("============");Dog dog2 = new Dog("大黄", 2);System.out.println("dog2的hashcode=" + dog2.hashCode());dog2.info();}
}class Dog{ //类String name;int age;// public Dog(String dName, int  dAge){//构造器//     name = dName;//     age = dAge;// }//如果我们构造器的形参,能够直接写成属性名,就更好了//但是出现了一个问题,根据变量的作用域原则//构造器的 name 是局部变量,而不是属性//构造器的 age  是局部变量,而不是属性//==> 引出this关键字来解决public Dog(String name, int  age){//构造器//this.name 就是当前对象的属性namethis.name = name;//this.age 就是当前对象的属性agethis.age = age;System.out.println("this.hashCode=" + this.hashCode());}public void info(){//成员方法,输出属性x信息System.out.println("this.hashCode=" + this.hashCode());System.out.println(name + "\t" + age + "\t");}
}

java虚拟机会给每个对象分配this, 代表当前对象。

this.name 就是当前对象属性name。

11.1深入理解this

隐藏的this指向自己的堆地址。

11.2this 的注意事项和使用细节

  1. this 关键字可以用来访问本类的属性、方法、构造器

  2. this 用于区分当前类的属性和局部变量

  3. 访问成员方法的语法:this.方法名(参数列表);

  4. 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一条语句)

    public class ThisDetail { //编写一个main方法public static void main(String[] args) {// T t1 = new T();// t1.f2();T t2 = new T();t2.f3();}
    }class T {String name = "jack";int num = 100;/*细节: 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)注意: 访问构造器语法:this(参数列表); 必须放置第一条语句!!!*/public T() {//这里去访问 T(String name, int age) 构造器this("jack", 100);System.out.println("T() 构造器");}public T(String name, int age) {System.out.println("T(String name, int age) 构造器");}//this关键字可以用来访问本类的属性public void f3() {String name = "smith";//传统方式(按照就近原则,有局部变量先访问局部变量)System.out.println("name=" + name + " num=" + num);//smith  100//也可以使用this访问属性(准确地就访问属性)System.out.println("name=" + this.name + " num=" + this.num);//jack 100}//细节: 访问成员方法的语法:this.方法名(参数列表);public void f1() {System.out.println("f1() 方法..");}public void f2() {System.out.println("f2() 方法..");//调用本类的 f1//第一种方式f1();//第二种方式this.f1();}}
    
  5. this 不能在类定义的外部使用,只能在类定义的方法中使用

11.3this 的案例

public class TestPerson { //编写一个main方法public static void main(String[] args) {Person p1 = new Person("mary", 20);Person p2 = new Person("mary", 20);System.out.println("p1和p2比较的结果=" + p1.compareTo(p2));}
}/*
定义Person类,里面有name、age属性,并提供compareTo比较方法,
用于判断是否和另一个人相等,提供测试类TestPerson用于测试, 
名字和年龄完全一样,就返回true, 否则返回false*/
class Person {String name;int age;//构造器public Person(String name, int age) {this.name = name;this.age = age;}//compareTo比较方法public boolean compareTo(Person p) {return this.name.equals(p.name) && this.age == p.age;}
}

12.IDEA 常用快捷键

  1. 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d
  2. 复制当前行, 自己配置 ctrl + alt + 向下光标
  3. 补全代码 alt + /
  4. 添加注释和取消注释 ctrl + /
  5. 导入该行需要的类先配置auto import , 然后使用 alt+enter 即可
  6. 快速格式化代码 ctrl + alt + L
  7. 快速运行程序自己定义 alt + R
  8. 生成构造器等 alt + insert [提高开发效率]
  9. 查看一个类的层级关系 ctrl + H
  10. 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法
  11. 自动的分配变量名, 通过在后面加.var

13.包

13.1包的三大作用

区分相同名字的类

当类很多时,可以很好的管理类[看Java API文档]

控制访问范围

13.2包基本语法

package com.hspedu;

说明:

package关键字,表示打包

com.hspedu:表示包名

13.3包的本质分析

包的本质实际上就是创建不同的文件夹/目录来保存类文件

13.4包的命名

命名规则

只能包含数字、字母、下划线、小圆点.,但不能用数字开头,不能是关键字或保留字。
命名规范

一般是小写字母+小圆点

一般是 com.公司名.项目名.业务模块名

例如:

com.sina.crm.user //用户模块

com.sina.crm.order //订单模块

com.sina.crm.utils //工具类

13.5常用的包

一个包下,包含很多的类,java 中常用的包有:

  1. java.lang.* //lang 包是基本包,默认引入,不需要再引入.
  2. java.util.* //util 包,系统提供的工具包, 工具类,使用Scanner
  3. java.net.* //网络包,网络开发
  4. java.awt.* //是做java 的界面开发,GUI

13.6如何引入包

语法: import 包;

我们引入一个包的主要目的是要使用该包下的类

比如

import java.util.Scanner;  //就只是引入一个类Scanner.import java.util.*;//表示将java.util包所有都引入

建议:我们需要使用到哪个类,就导入哪个类即可,不建议使用*导入

13.7注意事项和使用细节

  1. package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
  2. import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
//package的作用是声明当前类所在的包,需要放在类(或者文件)的最上面,
// 一个类中最多只有一句package
package com.hspedu.pkg;//import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求
import java.util.Scanner;
import java.util.Arrays;

14.访问修饰符

14.1基本介绍

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别: 用public 修饰,对外公开
  2. 受保护级别: 用protected 修饰, 对子类和同一个包中的类公开
  3. 默认级别: 没有修饰符号,向同一个包的类公开.
  4. 私有级别: 用private修饰,只有类本身可以访问,不对外公开.

14.2使用的注意事项

  1. 修饰符可以用来修饰类中的属性,成员方法以及类

  2. 只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。

  3. 成员方法的访问规则和属性完全一样.

15.面向对象编程三大特征

15.1基本介绍

面向对象编程有三大特征:封装、继承和多态。

15.2封装介绍

封装(encapsulation)就是把抽象出的数据 [属性] 和对数据的操作 [方法] 封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作 [方法] ,才能对数据进行操作。

15.3封装的理解和好处

隐藏实现细节: 方法(连接数据库) <-- 调用(传入参数)

可以对数据进行验证,保证安全合理

Person {name, age}
Person p = new Person();
p.name = "jack" ;
p.age= 1200;

15.4封装的实现步骤(三步)

  1. 将属性进行私有化private【不能直接修改属性】

  2. 提供一个公共的(public)set方法,用于对属性判断并赋值

    public void setXxx(类型参数名){//Xxx表示某个属性//加入数据验证的业务逻辑属性=参数名;
    }
    
  3. 提供一个公共的 (public)get 方法,用于获取属性的值

    public 数据类型 getXxx(){ //权限判断,Xxx某个属性return xx;
    }
    

15.5快速入门案例

package com.hspedu.encap;public class Encapsulation01 {public static void main(String[] args) {//如果要使用快捷键alt+r, 需要先配置主类//第一次,我们使用鼠标点击形式运算程序,后面就可以用Person person = new Person();person.setName("韩顺平");person.setAge(30);person.setSalary(30000);System.out.println(person.info());System.out.println(person.getSalary());//如果我们自己使用构造器指定属性Person smith = new Person("smith", 80, 50000);System.out.println("====smith的信息======");System.out.println(smith.info());}
}
/*
那么在java中如何实现这种类似的控制呢?
请大家看一个小程序(com.hspedu.encap: Encapsulation01.java),
不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认
年龄, 必须在 1-120, 年龄, 工资不能直接查看 , name的长度在 2-6字符 之间*/
class Person {public  String name; //名字公开private int age; //age 私有化private double salary; //..public void say(int n,String name) {}//构造器 alt+insertpublic Person() {}//有三个属性的构造器public Person(String name, int age, double salary) {
//        this.name = name;
//        this.age = age;
//        this.salary = salary;//我们可以将set方法写在构造器中,这样仍然可以验证setName(name);setAge(age);setSalary(salary);}// 自己写setXxx 和 getXxx 太慢,我们使用快捷键 Generate --> Getter and Setter// 然后根据要求来完善我们的代码.public String getName() {return name;}public void setName(String name) {// 加入对数据的校验,相当于增加了业务逻辑if(name.length() >= 2 && name.length() <=6 ) {this.name = name;}else {System.out.println("名字的长度不对,需要(2-6)个字符,默认名字");this.name = "无名人";}}public int getAge() {return age;}public void setAge(int age) {//判断if(age >= 1 && age <= 120) {//如果是合理范围this.age = age;} else {System.out.println("你设置年龄不对,需要在 (1-120), 给默认年龄18 ");this.age = 18;//给一个默认年龄}}public double getSalary() {//可以这里增加对当前对象的权限判断return salary;}public void setSalary(double salary) {this.salary = salary;}//写一个方法,返回属性信息public String info() {return "信息为 name=" + name  + " age=" + age + " 薪水=" + salary;}
}

15.6将构造器和setXxx 结合

可以将set方法写在构造器中,这样可以保证验证。

public Person(String name, int age, double salary) {// this.name = name;// this.age = age;// this.salary = salary;//我们可以将set 方法写在构造器中,这样仍然可以验证setName(name);setAge(age);setSalary(salary);
}

16.面向对象编程-继承

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends 来声明继承父类即可。

16.1继承的基本语法

class 子类 extends 父类 {
}

1)子类就会自动拥有父类定义的属性和方法

2)父类又叫超类,基类。

3)子类又叫派生类。

16.2继承的深入讨论/细节问题

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
  2. 子类必须调用父类的构造器,完成父类的初始化。先调用父类构造器,再调用子类构造器。
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过.
  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
  5. super 在使用时,必须放在构造器第一行( super 只能在构造器中使用 )
  6. super() 和this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  7. java 所有类都是Object 类的子类, Object 是所有类的基类.
  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到Object 类(顶级父类)
  9. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
    思考:如何让A 类继承B类和C类? 方法:A 继承B, B 继承C。
  10. 不能滥用继承,子类和父类之间必须满足is-a 的逻辑关系
package com.hspedu.extend_;import java.util.Arrays;//输入ctrl + H 可以看到类的继承关系
public class Sub extends Base { //子类public Sub(String name, int age) {//1. 要调用父类的无参构造器, 如下或者什么都不写,默认就是调用super()//super();//父类的无参构造器//2. 要调用父类的 Base(String name) 构造器//super("hsp");//3. 要调用父类的 Base(String name, int age) 构造器super("king", 20);//细节:super在使用时,必须放在构造器第一行//细节: super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器//this() 不能再使用了System.out.println("子类Sub(String name, int age)构造器被调用....");}public Sub() {//无参构造器//super(); //默认调用父类的无参构造器super("smith", 10);System.out.println("子类Sub()构造器被调用....");}//当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器public Sub(String name) {super("tom", 30);//do nothing...System.out.println("子类Sub(String name)构造器被调用....");}public void sayOk() {//子类方法//非私有的属性和方法可以在子类直接访问//但是私有属性和方法不能在子类直接访问System.out.println(n1 + " " + n2 + " " + n3);test100();test200();test300();//test400();错误//要通过父类提供公共的方法去访问System.out.println("n4=" + getN4());callTest400();//}}

16.3继承的本质分析!

我们着一个案例来分析当子类继承父类,创建子类对象时,内存中到底发生了什么?

当子类对象创建好后,建立查找的关系

  1. 最先加载父类,分别是Object类,然后加载Grandpa,再Father,最后Son。

  2. 然后再分配堆空间:不同类的相同变量名不会冲突,堆中空间不同。

  3. 最后Son对象(0x11都是)返回给main中的引用。

那么最后输出什么呢?(还是就近原则

package com.hspedu.extend_;/*** 讲解继承的本质*/
public class ExtendsTheory {public static void main(String[] args) {Son son = new Son();//内存的布局//?-> 这时请大家注意,要按照查找关系来返回信息//(1) 首先看子类是否有该属性//(2) 如果子类有这个属性,并且可以访问,则返回信息//(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)//(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到Object...System.out.println(son.name);//返回就是大头儿子//System.out.println(son.age);//返回的就是39//System.out.println(son.getAge());//返回的就是39System.out.println(son.hobby);//返回的就是旅游}
}class GrandPa { //爷类String name = "大头爷爷";String hobby = "旅游";
}class Father extends GrandPa {//父类String name = "大头爸爸";private int age = 39;public int getAge() {return age;}
}class Son extends Father { //子类String name = "大头儿子";
}

17.super 关键字

17.1基本介绍

super 代表父类的引用,用于访问父类的属性、方法、构造器

17.2基本语法

  1. 访问父类的属性,但不能访问父类的private属性[案例]
    super.属性名;
  2. 访问父类的方法,不能访问父类的private方法
    super.方法名(参数列表);
  3. 访问父类的构造器:
    super(参数列表);只能放在构造器的第一句,只能出现一句!
package com.hspedu.super_;public class A extends Base{//4个属性//public int n1 = 100;protected int n2 = 200;int 3 = 300;private int n4 = 400;public A() {}public A(String name) {}public A(String name, int age) {}//    public void cal() {
//        System.out.println("A类的cal() 方法...");
//    }public void test100() {}protected void test200() {}void test300() {}private void test400() {}
}

cal() 和 this.cal() 相同,就近原则。
super.cal() 的顺序是直接查找父类,其他的规则一样

package com.hspedu.super_;public class B extends A {public int n1 = 888;//编写测试方法public void test() {//super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;// 如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->CSystem.out.println("super.n1=" + super.n1);super.cal();}//访问父类的属性 , 但不能访问父类的private属性 [案例]super.属性名public void hi() {System.out.println(super.n1 + " " + super.n2 + " " + super.n3 );}public void cal() {System.out.println("B类的cal() 方法...");}public void sum() {System.out.println("B类的sum()");//希望调用父类-A 的cal方法//这时,因为子类B没有cal方法,因此我可以使用下面三种方式// !找cal方法时(cal() 和 this.cal()),顺序是:// (1)先找本类,如果有,则调用// (2)如果没有,则找父类(如果有,并可以调用,则调用)// (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object类// 提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access//      如果查找方法的过程中,没有找到,则提示方法不存在//cal();this.cal(); //等价 cal// !找cal方法(super.call()) 的顺序是直接查找父类,其他的规则一样//super.cal();//演示访问属性的规则// !n1 和 this.n1 查找的规则是//(1) 先找本类,如果有,则调用//(2) 如果没有,则找父类(如果有,并可以调用,则调用)//(3) 如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object类// 提示:如果查找属性的过程中,找到了,但是不能访问, 则报错, cannot access//      如果查找属性的过程中,没有找到,则提示属性不存在System.out.println(n1);System.out.println(this.n1);// !找n1 (super.n1) 的顺序是直接查找父类属性,其他的规则一样System.out.println(super.n1);}//访问父类的方法,不能访问父类的private方法 super.方法名(参数列表);public void ok() {super.test100();super.test200();super.test300();//super.test400();//不能访问父类private方法}//访问父类的构造器(这点前面用过):super(参数列表);只能放在构造器的第一句,只能出现一句!public  B() {//super();//super("jack", 10);super("jack");}
}

17.3super 给编程带来的便利/细节

  1. 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子
    类初始化)

  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须
    通过super。如果没有重名,使用super、this、直接访问是一样的效果!

  3. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用
    super去访问爷爷类的成员; 如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C,当然也需要遵守访问权限的相关规则

18.面向对象编程-多态

18.1多[多种]态[状态]基本介绍

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

18.2多态的具体体现

方法的多态

重写和重载就体现多态

对象的多态!

(1) 一个对象的编译类型和运行类型可以不一致

(2) 编译类型在定义对象时,就确定了,不能改变

(3) 运行类型是可以变化的.

(4) 编译类型看定义时 = 号的左边,运行类型看 = 号的右边

Animal animal = new Dog() // animal编译类型是Animal,运行类型Dog
animal = new Cat();// animal的运行类型变成了Cat,编译类型仍然是 Animal

18.3多态注意事项和细节讨论

多态的前提是:两个对象(类)存在继承关系

多态的向上转型
  1. 本质:父类的引用指向了子类的对象

  2. 语法:父类类型引用名=new子类类型();

  3. 特点:编译类型看左边,运行类型看右边。

    可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员;
    最终运行效果看子类的具体实现!
    (运行时看运行类型,例如找方法时就是采用就近原则)

    因为在编译阶段,能调用哪些成员,是由编译类型决定的。

多态向下转型

语法: 子类类型 引用名 = (子类类型) 父类引用;

  1. 只能强转父类的引用,不能强转父类的对象

  2. 要求父类的引用必须指向的是当前目标类型的对象

  3. 当向下转型后,可以调用子类类型中所有的成员

package com.hspedu.poly_.detail_;public class PolyDetail {public static void main(String[] args) {//向上转型: 父类的引用指向了子类的对象//语法:父类类型引用名 = new 子类类型();Animal animal = new Cat();Object obj = new Cat();//可以吗? 可以 Object 也是 Cat的父类//向上转型调用方法的规则如下://(1)可以调用父类中的所有成员(需遵守访问权限)//(2)但是不能调用子类的特有的成员//(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的//animal.catchMouse();错误//(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法//,然后调用,规则我前面我们讲的方法调用规则一致。animal.eat();//猫吃鱼..animal.run();//跑animal.show();//hello,你好animal.sleep();//睡//老师希望,可以调用Cat的 catchMouse方法//多态的向下转型//(1)语法:子类类型 引用名 =(子类类型)父类引用;//问一个问题? cat 的编译类型 Cat,运行类型是 CatCat cat = (Cat) animal;cat.catchMouse();//猫抓老鼠//(2)要求父类的引用必须指向的是当前目标类型的对象// animal 本来创建时就指向 cat对象// 后面 animal 向下转型 cat 指向 cat对象Dog dog = (Dog) animal;//可以吗? 错误!System.out.println("ok~~");}
}
属性没有重写之说

属性没有重写之说!属性的值看编译类型

package com.hspedu.poly_.detail_;public class PolyDetail02 {public static void main(String[] args) {//属性没有重写之说!属性的值看编译类型Base base = new Sub();//向上转型System.out.println(base.count);// ? 看编译类型 10Sub sub = new Sub();System.out.println(sub.count);//?  20}
}class Base { //父类int count = 10;//属性
}
class Sub extends Base {//子类int count = 20;//属性
}
instanceOf 比较操作符

用于判断对象的 运行类型 是否为 XX 类型 或 XX 类型的子类型

package com.hspedu.poly_.detail_;public class PolyDetail03 {public static void main(String[] args) {BB bb = new BB();System.out.println(bb instanceof  BB);// trueSystem.out.println(bb instanceof  AA);// true//aa 编译类型 AA, 运行类型是BB//BB是AA子类AA aa = new BB();System.out.println(aa instanceof AA); // trueSystem.out.println(aa instanceof BB); // trueObject obj = new Object();System.out.println(obj instanceof AA);//falseString str = "hello";//System.out.println(str instanceof AA);System.out.println(str instanceof Object);//true}
}class AA {} //父类
class BB extends AA {}//子类

18.4java 的动态绑定机制!!

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
,找不到再去父类中寻找。

package com.hspedu.poly_.dynamic_;public class DynamicBinding {public static void main(String[] args) {//a 的编译类型 A, 运行类型 BA a = new B();//向上转型System.out.println(a.sum()); //?40 -> 30 (20 + 10)System.out.println(a.sum1());//?30 -> 20 (10 + 10)}
}class A {//父类public int i = 10;//动态绑定机制:public int sum() {//父类sum()return getI() + 10;//20 + 10}public int sum1() {//父类sum1()return i + 10;//10 + 10}public int getI() {//父类getIreturn i;}
}class B extends A {//子类public int i = 20;//    public int sum() {
//        return i + 20;
//    }public int getI() {//子类getI()return i;}//    public int sum1() {
//        return i + 10;
//    }
}

18.5多态的应用

多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。

应用实例:现有一个继承结构如下:要求创建1 个Person 对象、2 个Student 对象和2 个Teacher 对象, 统一放在数组中,并调用每个对象 say 方法.

应用实例升级:如何调用子类特有的方法,比如Teacher 有一个teach , Student 有一个study ,怎么调用?

package com.hspedu.poly_.polyarr_;public class PloyArray {public static void main(String[] args) {//应用实例:现有一个继承结构如下:要求创建1个Person对象、// 2个Student 对象和2个Teacher对象, 统一放在数组中,并调用每个对象say方法Person[] persons = new Person[5];persons[0] = new Person("jack", 20);persons[1] = new Student("mary", 18, 100);persons[2] = new Student("smith", 19, 30.1);persons[3] = new Teacher("scott", 30, 20000);persons[4] = new Teacher("king", 50, 25000);//循环遍历多态数组,调用sayfor (int i = 0; i < persons.length; i++) {//老师提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况有JVM来判断System.out.println(persons[i].say());//动态绑定机制// 这里聪明. 使用 类型判断 + 向下转型.!!!!if(persons[i]  instanceof  Student) {//判断person[i] 的运行类型是不是StudentStudent student = (Student)persons[i];//向下转型student.study();//小伙伴也可以使用一条语句 ((Student)persons[i]).study();} else if(persons[i] instanceof  Teacher) {Teacher teacher = (Teacher)persons[i];teacher.teach();} else if(persons[i] instanceof  Person){//System.out.println("你的类型有误, 请自己检查...");} else {System.out.println("你的类型有误, 请自己检查...");}}}
}
多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型应用实例

定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法

测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]

测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法

package com.hspedu.poly_.polyarr_;public class Person {//父类private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}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 say() {//返回名字和年龄return name + "\t" + age;}
}
package com.hspedu.poly_.polyarr_;public class Student extends Person {private double score;public Student(String name, int age, double score) {super(name, age);this.score = score;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}//重写父类say@Overridepublic String say() {return "学生 " + super.say() + " score=" + score;}//特有的方法public void study() {System.out.println("学生 " + getName() + " 正在学java...");}
}
package com.hspedu.poly_.polyarr_;public class Teacher extends Person {private double salary;public Teacher(String name, int age, double salary) {super(name, age);this.salary = salary;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}//写重写父类的say方法@Overridepublic String say() {return "老师 " + super.say() + " salary=" + salary;}//特有方法public void teach() {System.out.println("老师 " + getName() + " 正在讲java课程...");}
}
package com.hspedu.poly_.polyarr_;public class PloyArray {public static void main(String[] args) {//应用实例:现有一个继承结构如下:要求创建1个Person对象、// 2个Student 对象和2个Teacher对象, 统一放在数组中,并调用每个对象say方法Person[] persons = new Person[5];persons[0] = new Person("jack", 20);persons[1] = new Student("mary", 18, 100);persons[2] = new Student("smith", 19, 30.1);persons[3] = new Teacher("scott", 30, 20000);persons[4] = new Teacher("king", 50, 25000);//循环遍历多态数组,调用sayfor (int i = 0; i < persons.length; i++) {//老师提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况有JVM来判断System.out.println(persons[i].say());//动态绑定机制//这里大家聪明. 使用 类型判断 + 向下转型.if(persons[i]  instanceof  Student) {//判断person[i] 的运行类型是不是StudentStudent student = (Student)persons[i];//向下转型student.study();//小伙伴也可以使用一条语句 ((Student)persons[i]).study();} else if(persons[i] instanceof  Teacher) {Teacher teacher = (Teacher)persons[i];teacher.teach();} else if(persons[i] instanceof  Person){//System.out.println("你的类型有误, 请自己检查...");} else {System.out.println("你的类型有误, 请自己检查...");}}}
}

19.Object 类详解

19.1equals 方法

==和equals 的对比!!!

==是一个比较运算符

==:既可以判断基本类型,又可以判断引用类型

  • ==:如果判断基本类型,判断的是值是否相等。示例: int i=10; double d=10.0;

  • ==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

equals:是Object类中的方法,只能判断引用类型,看Jdk源码。(方法:光标放在方法上。然后按 ctrl + B 进行查看源码)

  • 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如 Integer,String【看看String和 Integer的equals源代码】
package com.hspedu.object_;public class Equals01 {public static void main(String[] args) {A a = new A();A b = a;A c = b;System.out.println(a == c);//trueSystem.out.println(b == c);//true// 编译类型是B,但是本质上还是一个地址指向aB bObj = a;System.out.println(bObj == c);//trueint num1 = 10;double num2 = 10.0;System.out.println(num1 == num2);// 基本数据类型,判断值是否相等//equals 方法,源码怎么查看.//把光标放在equals方法,直接输入ctrl+b//如果你使用不了. 自己配置. 即可使用./*//带大家看看Jdk的源码 String类的 equals方法//把Object的equals方法重写了,变成了比较两个字符串值是否相同public boolean equals(Object anObject) {if (this == anObject) {//如果是同一个对象return true;//返回true}if (anObject instanceof String) {//判断类型String anotherString = (String)anObject;//向下转型int n = value.length;if (n == anotherString.value.length) {//如果长度相同char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {//然后一个一个的比较字符if (v1[i] != v2[i])return false;i++;}return true;//如果两个字符串的所有字符都相等,则返回true}}return false;//如果比较的不是字符串,则直接返回false}*/"hello".equals("abc");//看看Object类的 equals 是/*//即Object 的equals 方法默认就是比较对象地址是否相同//也就是判断两个对象是不是同一个对象.public boolean equals(Object obj) {return (this == obj);}*//*//从源码可以看到 Integer 也重写了Object的equals方法,//变成了判断两个值是否相同public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}return false;}*/Integer integer1 = new Integer(1000);Integer integer2 = new Integer(1000);System.out.println(integer1 == integer2);//falseSystem.out.println(integer1.equals(integer2));//trueString str1 = new String("hspedu");String str2 = new String("hspedu");System.out.println(str1 == str2);//falseSystem.out.println(str1.equals(str2));//true}
}class B {}
class A extends B {}

19.2如何重写 equals 方法

应用实例: 判断两个Person 对象的内容是否相等,如果两个Person 对象的各个属性值都一样,则返回true,反之false。

查看:com.hspedu.object_ EqualsExercise01

19.3hashCode 方法

public int hashCode()

返回该对象的哈希码值。支持此方法是为了提高哈希表(例如java.util.Hashtable 提供的哈希表)的性能。

hashCode 的常规协定是:

  • 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
  • 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
  • 如果根据 equals(java.lang.Object)
    方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法
    要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。

实际上,由 Object 类定义的 hashCode
方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

返回:

此对象的一个哈希码值。

另请参见:

[equals(java.lang.Object)], [Hashtable]

总结
  1. 提高具有哈希结构的容器的效率!
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的(当然也可能存在碰撞)
  4. 哈希值主要根据地址号来的! 不能完全将哈希值等价于地址。(java跑在JVM上,无法真正拿到其内部地址)。
  5. 后面在集合,中hashCode 如果需要的话,也会重写, 在讲解集合时,具体看如何重写hashCode()代码。

19.4toString 方法

public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
  1. 基本介绍
    默认返回:全类名+@+哈希值的十六进制,子类往往重写toString 方法,用于返回对象的属性信息(全类名就是包名 + 类名)

  2. 重写toString 方法,打印对象或拼接对象时,都会自动调用该对象的toString 形式.

  3. 当直接输出一个对象时, toString 方法会被默认的调用, 比如System.out.println(monster) ;// 就会默认调用monster.toString()

20.finalize 方法

  1. 当对象被回收时,系统自动调用该对象的finalize 方法。子类可以重写该方法,做一些释放资源(数据库的连接,打开或者关闭文件)的操作。
  2. 什么时候被回收:当某个对象没有任何引用时,则jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize 方法。(当然并不是一有垃圾就立马回收,有对应的垃圾回收GC算法)。
  3. 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法), 也可以通过System.gc() 主动触发垃圾回收机制。
  4. 我们在实际开发中,几乎不会运用finalize , 所以更多就是为了应付面试。
package com.hspedu.object_;//演示 Finalize的用法
public class Finalize_ {public static void main(String[] args) {Car bmw = new Car("宝马");//这时 car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象, 在销毁对象前,会调用该对象的finalize方法//,程序员就可以在 finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)//,如果程序员不重写 finalize,那么就会调用 Object类的 finalize, 即默认处理//,如果程序员重写了 finalize, 就可以实现自己的逻辑bmw = null;System.gc();//主动调用垃圾回收器System.out.println("程序退出了....");}
}
class Car {private String name;//属性, 资源。。public Car(String name) {this.name = name;}//重写finalize@Overrideprotected void finalize() throws Throwable {System.out.println("我们销毁 汽车" + name );System.out.println("释放了某些资源...");}
}

20.1断点调试(debug)

在断点调试过程中,是运行状态,是以对象的运行类型来执行的.

A extends B; Bb = new A(); b.xx();

20.2断点调试介绍

断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug。

断点调试也能帮助我们查看java底层源代码的执行过程。

20.3断点调试的快捷键

F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)

F7:跳入方法内

F8: 逐行执行代码.

shift+F8: 跳出方法

21.类变量和类方法

21.1类变量-提出问题

在main方法中定义一个变量count,当一个小孩加入游戏后count++,最后个count 就记录有多少小孩玩游戏 。

问题分析:

count是一个独立于对象,很尴尬,以后我们访问count很麻烦,没有使用到OOP。因此,我们引出类变量/静态变量。

package com.hspedu.static_;public class ChildGame {public static void main(String[] args) {//定义一个变量 count, 统计有多少小孩加入了游戏int count = 0;Child child1 = new Child("白骨精");child1.join();//count++;child1.count++;Child child2 = new Child("狐狸精");child2.join();//count++;child2.count++;Child child3 = new Child("老鼠精");child3.join();//count++;child3.count++;//===========// 类变量,可以通过类名来访问System.out.println("共有" + Child.count  + " 小孩加入了游戏...");// 下面的代码输出什么?System.out.println("child1.count=" + child1.count);//3System.out.println("child2.count=" + child2.count);//3System.out.println("child3.count=" + child3.count);//3}
}class Child { //类private String name;// 定义一个变量 count ,是一个类变量(静态变量) static 静态!!!// 该变量最大的特点就是会被 Child 类的所有的对象实例共享!!!public static int count = 0;public Child(String name) {this.name = name;}public void join() {System.out.println(name + " 加入了游戏..");}
}

21.2类变量内存布局

https://blog.csdn.net/x_iya/article/details/81260154/

https://www.zhihu.com/question/59174759/answer/163207831

有些书说在方法区… jdk 版本有关系,记住两点:

(1) static变量是同一个类所有对象共享

(2) static类变量,在类加载的时候就生成了.静态变量是类加载的时候,就创建了,所以不用创建对象实例也能直接通过 类名.类变量名 访问。

21.3什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。

21.4如何定义类变量

定义语法:

访问修饰符static数据类型变量名;[推荐]

static访问修饰符数据类型变量名;

21.5如何访问类变量

类名.类变量名

或者对象名.类变量名【静态变量的访问修饰符的访问权限和范围和普通属性是一样的】

推荐使用:类名.类变量名;

21.6类变量使用注意事项

1.什么时候需要用类变量

当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name, staticfee)

2.类变量与实例变量(普通属性)区别

类变量是该类的所有对象共享的,而实例变量是每个对象独享的。

3.加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

4.类变量可以通过类名.类变量名或者对象名.类变量名来访问,但java设计者推荐我们使用类名.类变量名方式访问。【前提是满足访问修饰符的访问权限和范围】

5.实例变量不能通过类名.类变量名方式访问。

6.类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了.就可以使用类变量了。

7.类变量的生命周期是随类的加载开始,随着类消亡而销毁。

21.7类方法基本介绍

类方法也叫静态方法。形式如下:

访问修饰符 static 数据返回类型 方法名(){}【推荐】

static 访问修饰符 数据返回类型 方法名(){}

21.8类方法的调用

使用方式:

类名.类方法名或者对象名.类方法名

package com.hspedu.static_;public class StaticMethod {public static void main(String[] args) {//创建2个学生对象,叫学费Stu tom = new Stu("tom");//tom.payFee(100);Stu.payFee(100);//对不对?对Stu mary = new Stu("mary");//mary.payFee(200);Stu.payFee(200);//对//输出当前收到的总学费Stu.showFee();//300//如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用)//这时,把方法做成静态方法时非常合适System.out.println("9开平方的结果是=" + Math.sqrt(9));System.out.println(MyTools.calSum(10, 30));}
}
//开发自己的工具类时,可以将方法做成静态的,方便调用
class MyTools  {//求出两个数的和public static double calSum(double n1, double n2) {return  n1 + n2;}//可以写出很多这样的工具方法...
}
class Stu {private String name;//普通成员//定义一个静态变量,来累积学生的学费private static double fee = 0;public Stu(String name) {this.name = name;}// 说明// 1. 当方法使用了static修饰后,该方法就是静态方法// 2. 静态方法就可以访问静态属性/变量public static void payFee(double fee) {Stu.fee += fee;//累积到}public static void showFee() {System.out.println("总学费有:" + Stu.fee);}
}

21.9类方法经典的使用场景

当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法, 提高开发效率。

比如:

工具类中的方法utils。Math类、Arrays类、Collections集合类看下源码可以发现都是static方法。

21.10类方法使用注意事项和细节讨论

  1. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区∶类方法中无this的参数。普通方法中隐含着this的参数。

  2. 类方法可以通过类名调用,也可以通过对象名调用。普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。

  3. 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。

  4. 类方法(静态方法)中只能访问静态变量或静态方法。普通成员方法,既可以访问非静态成员,也可以访问静态成员!!

package com.hspedu.static_;public class StaticMethodDetail {public static void main(String[] args) {D.hi();//ok//非静态方法,不能通过类名调用//D.say();, 错误,需要先创建对象,再调用new D().say();//可以}
}
class D {private int n1 = 100;private static  int n2 = 200;public void say() {//非静态方法,普通方法}public static  void hi() {//静态方法,类方法//类方法中不允许使用和对象有关的关键字,//比如this和super。普通方法(成员方法)可以。//System.out.println(this.n1);}//类方法(静态方法)中 只能访问 静态变量 或静态方法//口诀:静态方法只能访问静态成员.public static void hello() {System.out.println(n2);System.out.println(D.n2);//System.out.println(this.n2);不能使用hi();//OK//say();//错误}//普通成员方法,既可以访问  非静态成员,也可以访问静态成员//小结: 非静态方法可以访问 静态成员和非静态成员public void ok() {//非静态成员System.out.println(n1);say();//静态成员System.out.println(n2);hello();}
}

练习:

package com.hspedu.static_;public class StaticExercise03 {
}class Person {private int id;private static int total = 0;public static void setTotalPerson(int total){// this.total = total;//错误,因为在static方法中,不可以使用this 关键字Person.total = total;}public Person() {//构造器total++;id = total;}//编写一个方法,输出total的值public static void m() {System.out.println("total的值=" + total);}
}
class TestPerson {public static void main(String[] args) {Person.setTotalPerson(3); // 这里没有调用构造器new Person(); // new了之后调用构造器,count++Person.m();// 最后 total的值就是4}
}

注意:

Person.setTotalPerson(3); 调用静态方法 这里还没有调用构造器

new Person(); new了之后才调用构造器,count++

因为构造器是在创建对象时完成对对象的初始化。

22.理解main 方法语法

22.1深入理解main 方法

解释main方法的形式: public static void main(String[] args){}

1.main方法时虚拟机调用

2.java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public

3.java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static

4.该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数,案例演示,接收参数.

5.java执行的程序参数1参数2参数。

23.代码块

23.1基本介绍

代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过包围起来。

但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用

23.2基本语法

[修饰符]{代码
};

说明注意;

  1. 修饰符可选,要写的话,也只能写static

  2. 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。

  3. 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)

  4. ;号可以写上,也可以省略。

23.3代码块的好处和案例演示

  1. 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作

  2. 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性

这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容,代码块调用的顺序优先于构造器。

package com.hspedu.codeblock_;public class CodeBlock01 {public static void main(String[] args) {Movie movie = new Movie("你好,李焕英");System.out.println("===============");Movie movie2 = new Movie("唐探3", 100, "陈思诚");}
}class Movie {private String name;private double price;private String director;// 3个构造器-》重载{System.out.println("电影屏幕打开...");System.out.println("广告开始...");System.out.println("电影正是开始...");};public Movie(String name) {System.out.println("Movie(String name) 被调用...");this.name = name;}public Movie(String name, double price) {this.name = name;this.price = price;}public Movie(String name, double price, String director) {System.out.println("Movie(String name, double price, String director) 被调用...");this.name = name;this.price = price;this.director = director;}
}

23.4代码块使用注意事项和细节讨论!!!

  1. static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象, 就执行一次。

  2. 类什么时候被加载

    • 创建对象实例时(new)
    • 创建子类对象实例,父类也会被加载
    • 使用类的静态成员时(静态属性,静态方法)
  3. 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。

    如果只是使用类的静态成员时,普通代码块并不会执行。(没有创建对象实例)

  4. 创建一个对象时,在一个类调用顺序是 (重点,难点)

    1. 调用静态代码块和静态属性初始化 (注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的先后顺序调用)

    2. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义先后顺序调用)

    3. 调用构造方法

  5. 构造器的最前面其实隐含了super()和调用普通代码块, 静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的。

  6. 我们看一下创建一个子类对象时(继承关系),他们的调用顺序如下:

    1. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)(类加载)

    2. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)(类加载)

    3. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

    4. 父类的构造方法

    5. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

    6. 子类的构造方法

  7. 静态代码块(本质上是静态方法)只能直接调用静态成员(静态属性和静态方法),普通代码块(本质上是普通方法)可以调用任意成员。

package com.hspedu.codeblock_;public class CodeBlockDetail04 {public static void main(String[] args) {//老师说明//(1) 进行类的加载//1.1 先加载 父类 A02 1.2 再加载 B02//(2) 创建对象//2.1 从子类的构造器开始//new B02();//对象new C02();}
}class A02 { //父类private static int n1 = getVal01();static {System.out.println("A02的一个静态代码块..");//(2)}{System.out.println("A02的第一个普通代码块..");//(5)}pulic int n3 = getVal02();//普通属性的初始化public static int getVal01() {System.out.println("getVal01");//(1)return 10;}public int getVal02() {System.out.println("getVal02");//(6)return 10;}public A02() {//构造器//隐藏//super()//普通代码和普通属性的初始化......System.out.println("A02的构造器");//(7)}}class C02 {private int n1 = 100;private static  int n2 = 200;private void m1() {}private static void m2() {}static {//静态代码块,只能调用静态成员//System.out.println(n1);错误System.out.println(n2);//ok//m1();//错误m2();}{//普通代码块,可以使用任意成员System.out.println(n1);System.out.println(n2);//okm1();m2();}
}class B02 extends A02 { //private static int n3 = getVal03();static {System.out.println("B02的一个静态代码块..");//(4)}public int n5 = getVal04();{System.out.println("B02的第一个普通代码块..");//(9)}public static int getVal03() {System.out.println("getVal03");//(3)return 10;}public int getVal04() {System.out.println("getVal04");//(8)return 10;}//一定要慢慢的去品..public B02() {//构造器//隐藏了//super()//普通代码块和普通属性的初始化...System.out.println("B02的构造器");//(10)// TODO Auto-generated constructor stub}
}

练习:

package com.hspedu.codeblock_;public class CodeBlockExercise02 {
}class Sample
{Sample(String s){System.out.println(s);}Sample(){System.out.println("Sample默认构造函数被调用");}
}
class Test{Sample sam1=new Sample("sam1成员初始化");//static Sample sam=new Sample("静态成员sam初始化 ");//static{System.out.println("static块执行");//if(sam==null)System.out.println("sam is null");}Test()//构造器{System.out.println("Test默认构造函数被调用");//}//主方法public static void  main(String  str[]){Test a=new Test();//无参构造器}}1. 静态成员sam 初始化
2. static 块执行
3. sam1 成员初始化
4. Test 默认构造函数被调用

24单例设计模式

24.1什么是设计模式

静态方法和属性的经典使用

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。

24.2什么是单例模式

  1. 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

  2. 单例模式有两种方式:1) 饿汉式 2) 懒汉式

饿汉式

步骤如下:

  1. 构造器私有化 =》防止直接new

  2. 类的内部创建对象

  3. 向外暴露一个静态的公共方法。getlnstance

饿汉式:有可能还没有用到这个对象,但是由于类的机制已经将对象创建好了。在线程还没出现之前就已经实例化了,因此饿汉式线程一定是安全的。

package com.hspedu.single_;public class SingleTon01 {public static void main(String[] args) {
//        GirlFriend xh = new GirlFriend("小红");
//        GirlFriend xb = new GirlFriend("小白");//通过方法可以获取对象GirlFriend instance = GirlFriend.getInstance();System.out.println(instance);// 都是同一个对象GirlFriend instance2 = GirlFriend.getInstance();System.out.println(instance2);System.out.println(instance == instance2);// T 同一个对象//System.out.println(GirlFriend.n1);}
}// 有一个类, GirlFriend
// 只能有一个女朋友
class GirlFriend {private String name;// public static  int n1 = 100;// 为了能够在静态方法中,返回 gf对象,需要将其修饰为static// 對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用.// 只要类加载了,就一定创建了gf对象private static GirlFriend gf = new GirlFriend("小红红");// 如何保障我们只能创建一个 GirlFriend 对象// 步骤[单例模式-饿汉式]// 1. 将构造器私有化// 2. 在类的内部直接创建对象(该对象是static)// 3. 提供一个公共的static方法,返回 gf 对象private GirlFriend(String name) {System.out.println("構造器被調用.");this.name = name;}// 用static的目的就是在不创建对象的前提下直接调用public static GirlFriend getInstance() {return gf;}@Overridepublic String toString() {return "GirlFriend{" +"name='" + name + '\'' +'}';}
}
懒汉式

懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象。

懒汉式可能会存在线程安全的问题。

package com.hspedu.single_;/*** 演示懶漢式的單例模式*/
public class SingleTon02 {public static void main(String[] args) {//new Cat("大黃");//System.out.println(Cat.n1);Cat instance = Cat.getInstance();System.out.println(instance);//再次調用getInstanceCat instance2 = Cat.getInstance();System.out.println(instance2);System.out.println(instance == instance2);//T}
}//希望在程序運行過程中,只能創建一個Cat對象
//使用單例模式
class Cat {private String name;public static  int n1 = 999;private static Cat cat ; //默認是null//步驟//1.仍然構造器私有化//2.定義一個static靜態屬性對象//3.提供一個public的static方法,可以返回一個Cat對象//4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象//  從而保證了單例private Cat(String name) {System.out.println("構造器調用...");this.name = name;}public static Cat getInstance() {if(cat == null) {//如果還沒有創建cat對象cat = new Cat("小可愛");}return cat;}@Overridepublic String toString() {return "Cat{" +"name='" + name + '\'' +'}';}
}
比较
  1. 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。

  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善一把)。

  3. 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。

  4. 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式.

25.final 关键字

25.1基本介绍

final中文意思:最后的,最终的.

final可以修饰类、属性、方法和局部变量

在某些情况下,程序员可能有以下需求,就会使用到final:

  1. 当不希望类被继承时,可以用final修饰.

  2. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【案例演示:访问修饰符final返回类型方法名】

  3. 当不希望类的的某个属性的值被修改,可以用final修饰.(例如: public final double TAX RATE=0.08)

  4. 当不希望某个局部变量被修改,可以使用final修饰(例如: final double TAX RATE=0.08)

25.2final 使用注意事项和细节讨论

  1. final修饰的属性又叫常量,一般用 XX_XX_XX (大写)来命名

  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:

    定义时:如public final double TAX_RATE=0.08;

    在构造器中

    在代码块中

    class AA {
    /*
    1. 定义时:如public final double TAX_RATE=0.08;
    2. 在构造器中
    3. 在代码块中
    */
    public final double TAX_RATE = 0.08;//1.定义时赋值
    public final double TAX_RATE2 ;
    public final double TAX_RATE3 ;
    public AA() {//构造器中赋值TAX_RATE2 = 1.1;}{//在代码块赋值TAX_RATE3 = 8.8;}
    }
    
  3. 如果final修饰的属性是静态的,则初始化的位置只能是

    ①定义时

    ②在静态代码块(不能在构造器中赋值。因为构造器是在对象创建的时候才会进行赋值)

  4. final类不能继承,但是可以实例化对象。(实例化没问题)

  5. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。(子类用是没问题的,虽然不能重写)

  6. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。(因为类既然不能被继承,也就相应无法被重写)。

  7. final不能修饰构造方法(即构造器)。

  8. final和static 往往搭配使用,效率更高,因为不会导致类加载,底层编译器做了优化处理。

  9. 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。

26.抽象类

26.1引出

当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。

所谓抽象方法就是没有实现的方法,所谓没有实现就是指,没有方法体。

当一个类中存在抽象方法时,需要将该类声明为abstract 类,一般来说,抽象类会被继承,由其子类来实现抽象方法。

abstract class Animal {private String name;public Animal(String name) {this.name = name;}public abstract void eat()  ;
}

26.2抽象类的介绍

1)用abstract关键字来修饰一个类时,这个类就叫抽象类访问修饰符

2)用abstract关键字来修饰一个方法时,这个方法就是抽象方法

访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体

3)抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类。

4)抽象类是考官比较爱问的知识点,在框架和设计模式使用较多。

26.3抽象类使用的注意事项和细节讨论

1)抽象类不能被实例化

2)抽象类不一定要包含abstract方法。也就是说, 抽象类可以没有abstract方法。

3)一旦类包含了abstract方法,则这个类必须声明为abstract。

4)abstract只能修饰类和方法,不能修饰属性和其它的。

5)抽象类可以有任意成员【抽象类本质还是类】,比如: 非抽象方法、构造器、静态属性等等。

6)抽象方法不能有主体,即不能实现

7)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。

8)抽象方法不能使用private、final和 static来修饰,因为这些关键字都是和重写相违背的。

27.抽象类最佳实践-模板设计模式

27.1基本介绍

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

27.2模板设计模式能解决的问题

1)当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
2)编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式.

27.3最佳实践

需求:

有多个类,完成不同的任务job

要求统计得到各自完成任务的时间

package com.hspedu.abstract_;abstract public class Template { //抽象类-模板设计模式public abstract void job();//抽象方法public void calculateTime() {//实现方法,调用job方法//得到开始的时间long start = System.currentTimeMillis();job(); //动态绑定机制//得的结束的时间long end = System.currentTimeMillis();System.out.println("任务执行时间 " + (end - start));}
}

以上就是把不确定的部分暴露出去,让子类去实现。

28.接口

28.1基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。语法:

interface 接口名{//属性//抽象方法(接口中可以省略abstract关键字)(在jdk8后还可以有静态方法和默认方法)
}class 类名 implements 接口 {// 自己属性;// 自己方法;// 必须实现的接口的抽象方法
}

小结:

接口是更加抽象的类。抽象类里的方法可以有方法体,接口里的所有方法都没有方法体(jdk7.0)。接口体现了程序设计的多态和高内聚低偶合的设计思想。

特别说明:Jdk8.0后接口类可以有静态方法(static),默认方法(default),也就是说接口中可以有方法的具体实现入。

28.2深入讨论

说现在有一个项目经理(段玉),管理三个程序员,功能开发一个软件.为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现。

通过接口,不仅可以统一方法名,同时在调用时只需要根据接口识别即可。

package com.hspedu.interface_;public interface DBInterface { //项目经理public void connect();//连接方法public void close();//关闭连接
}
package com.hspedu.interface_;
//A程序
public class MysqlDB implements DBInterface {@Overridepublic void connect() {System.out.println("连接mysql");}@Overridepublic void close() {System.out.println("关闭mysql");}
}
package com.hspedu.interface_;//B程序员连接Oracle
public class OracleDB implements DBInterface{@Overridepublic void connect() {System.out.println("连接oracle");}@Overridepublic void close() {System.out.println("关闭oracle");}
}
package com.hspedu.interface_;public class Interface03 {public static void main(String[] args) {MysqlDB mysqlDB = new MysqlDB();t(mysqlDB);OracleDB oracleDB = new OracleDB();t(oracleDB);}public static void t(DBInterface db) {db.connect();db.close();}
}

28.3注意事项和细节

  1. 接口不能被实例化(new)

  2. 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修
    饰。void aaa(); 实际上是abstract void aa();(同理,不写public也是默认public方法,因此实现时该方法不写public会报错。)

  3. 一个普通类实现接口,就必须将该接口的所有方法都实现。

  4. 抽象类实现接口,可以不用实现接口的方法。

  5. 一个类同时可以实现多个接口

    class Timer implements IA, IB{ }
    
  6. 接口中的属性,只能是final的,而且是 public static final修饰符。比如:int a=1;实际上是public static final int a=1; (必须初始化)

  7. 接口中属性的访问形式:接口名.属性名

  8. 接口不能继承其它的类,但是可以继承多个别的接口。(接口无法实现接口)

    interface A extends B,C{}
    
  9. 接口的修饰符只能是public和默认,这点和类的修饰符是一样的。

28.4实现接口VS继承类

当子类继承了父类,就自动的拥有父类的功能,如果子类需要扩展功能,可以通过实现接口的方式扩展。可以理解 实现接口 是对 java 单继承机制的一种补充。

  1. 接口和继承解决的问题不同

    继承的价值主要在于:解决代码的复用性和可维护性。

  2. 接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即更加的灵活

接口比继承更加灵活:继承是满足is - a的关系,而接口只需满足 like - a的关系。

接口在一定程度上实现代码解耦[即:接口规范性+动态绑定机制]

28.5接口的多态特性

  1. 多态参数

    在前面的Usb接口案例,UsbInterface usb,既可以接收手机对象,又可以接收相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)。

    package com.hspedu.interface_;public class InterfacePolyParameter {public static void main(String[] args) {//接口的多态体现//接口类型的变量 if01 可以指向 实现了IF接口类的对象实例IF if01 = new Monster();if01 = new Car();// 继承体现的多态// 父类类型的变量 a 可以指向 继承AAA的子类的对象实例AAA a = new BBB();a = new CCC();}
    }interface IF {}
    class Monster implements IF{}
    class Car implements  IF{}class AAA {}
    class BBB extends AAA {}
    class CCC extends AAA {}
    
  2. 多态数组

演示一个案例:给Usb数组中,存放 Phone 和相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone特有方法call。

package com.hspedu.interface_;public class InterfacePolyArr {public static void main(String[] args) {//多态数组 -> 接口类型数组Usb[] usbs = new Usb[2];usbs[0] = new Phone_();usbs[1] = new Camera_();/*给Usb数组中,存放 Phone 和 相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,还需要调用Phone 特有方法 call*/for(int i = 0; i < usbs.length; i++) {usbs[i].work();//动态绑定..//和前面一样,我们仍然需要进行类型的向下转型if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_((Phone_) usbs[i]).call();}}}
}interface Usb{void work();
}
class Phone_ implements Usb {public void call() {System.out.println("手机可以打电话...");}@Overridepublic void work() {System.out.println("手机工作中...");}
}
class Camera_ implements Usb {@Overridepublic void work() {System.out.println("相机工作中...");}
}
  1. 接口存在多态传递现象

    package com.hspedu.interface_;/*** 演示多态传递现象*/
    public class InterfacePolyPass {public static void main(String[] args) {//接口类型的变量可以指向,实现了该接口的类的对象实例IG ig = new Teacher();//如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口//那么,实际上就相当于 Teacher 类也实现了 IH接口.//这就是所谓的 接口多态传递现象.IH ih = new Teacher();}
    }interface IH {void hi();
    }
    interface IG extends IH{ }
    class Teacher implements IG {@Overridepublic void hi() {}
    }
    

29.内部类

如果定义类在局部位置(方法中/代码块) (1) 局部内部类 (2) 匿名内部类
定义在成员位置 (1) 成员内部类 (2) 静态内部类

29.1基本介绍

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。

是我们类的第五大成员(类的五大成员:属性、方法、构造器、代码块、内部类),内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类。

29.2基本语法

class Outer{ // 外部类class Inner{// 内部类}
}
class Other{// 外部其他类
}

29.3内部类的分类

定义在外部类局部位置上( 比如方法内 ):

  1. 局部内部类 ( 有类名 )

  2. 匿名内部类 ( 没有类名,重点!!! )

定义在外部类的成员位置上:

  1. 成员内部类 ( 没用 static 修饰 )

  2. 静态内部类 ( 使用 static 修饰 )

29.4局部内部类的使用

说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名

1.可以直接访问外部类的所有成员,包含私有的。

2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final。

3.作用域:仅仅在定义它的方法或代码块中

4.局部内部类访问外部类的成员[访问方式:直接访问]

5.外部类访问局部内部类的成员

访问方式: 创建对象,再访问 (注意:必须在作用域内)

小结:

(1)局部内部类定义在方法中/代码块
(2)作用域在方法体或者代码块中
(3)本质仍然是一个类

6.外部其他类不能访问局部内部类(因为局部内部类地位是一个局部变量)。

7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。

这里 外部类名.this 本质上就是外部类的对象,即哪个对象调用了n2,那么 外部类名.this 就指向哪个对象。

System.out.printin(""外部类的n2=+外部类名.this.n2);
package com.hspedu.innerclass;
/*** 演示局部内部类的使用*/
public class LocalInnerClass {//public static void main(String[] args) {//演示一遍Outer02 outer02 = new Outer02();outer02.m1();System.out.println("outer02的hashcode=" + outer02);}
}class Outer02 {//外部类private int n1 = 100;private void m2() {System.out.println("Outer02 m2()");}//私有方法public void m1() {//方法//1.局部内部类是定义在外部类的局部位置,通常在方法//3.不能添加访问修饰符,但是可以使用final 修饰//4.作用域 : 仅仅在定义它的方法或代码块中final class Inner02 {//局部内部类(本质仍然是一个类)//2.可以直接访问外部类的所有成员,包含私有的private int n1 = 800;public void f1() {//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,//   使用 外部类名.this.成员)去访问//  Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);System.out.println("Outer02.this hashcode=" + Outer02.this);m2();}}//6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可Inner02 inner02 = new Inner02();inner02.f1();}
}

29.5匿名内部类的使用!!!

(1)本质是类 (2) 内部类 (3) 该类没有名字 (4) 同时还是一个对象

说明: 匿名内部类是定义在外部类的局部位置, 比如方法中, 并且没有类名

1.匿名内部类的基本语法

new 类或接口 (参数列表){类体
);
package com.hspedu.innerclass;/*** 演示匿名内部类的使用*/
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}
}class Outer04 { //外部类private int n1 = 10;//属性public void method() {//方法//基于!!!接口!!!的匿名内部类//解读//1.需求:想使用IA接口,并创建对象//2.传统方式,是写一个类,实现该接口,并创建对象//3.需求是 Tiger/Dog 类只是使用一次,后面再不使用//4. 可以使用匿名内部类来简化开发//5. tiger的编译类型 ? IA//6. tiger的运行类型 ? 就是匿名内部类  Outer04$1/*我们看底层 会分配 类名 Outer04$1class Outer04$1 implements IA {@Overridepublic void cry() {System.out.println("老虎叫唤...");}}*///7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址//   返回给 tiger//8. 匿名内部类使用一次,就不能再使用, 但是tiger这个对象就没有限制了。IA tiger = new IA() {@Overridepublic void cry() {System.out.println("老虎叫唤...");}};System.out.println("tiger的运行类型=" + tiger.getClass());tiger.cry();tiger.cry();tiger.cry();//        IA tiger = new Tiger();
//        tiger.cry();// 演示基于!!!类!!!的匿名内部类//分析//1. father 编译类型 Father//2. father 运行类型 Outer04$2//3. 底层会创建匿名内部类/*具体的实现代码与注释中的代码近似等价class Outer04$2 extends Father{@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}}*///4. 同时也直接返回了 匿名内部类 Outer04$2的对象//5. 注意("jack") 参数列表会传递给 Father 构造器Father father = new Father("jack"){@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}};System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2father.test();//基于!!!抽象类!!!的匿名内部类Animal animal = new Animal(){@Overridevoid eat() {System.out.println("小狗吃骨头...");}};animal.eat();}
}interface IA {//接口public void cry();
}
//class Tiger implements IA {
//
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤...");
//    }
//}
//class Dog implements  IA{
//    @Override
//    public void cry() {
//        System.out.println("小狗汪汪...");
//    }
//}class Father { //类public Father(String name) { //构造器System.out.println("接收到name=" + name);}public void test() { //方法}
}abstract class Animal { //抽象类abstract void eat();
}

2.匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义.同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。

3.可以直接访问外部类的所有成员,包含私有的。

4、不能添加访问修饰符,因为它的地位就是一个局部变量。

5.作用域:仅仅在定义它的方法或代码块中。

6.匿名内部类—访问---->外部类成员[访问方式:直接访问]

7.外部其他类—不能访问----->匿名内部类(因为匿名内部类地位是一个局部变量)

8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

package com.hspedu.innerclass;public class AnonymousInnerClassDetail {public static void main(String[] args) {Outer05 outer05 = new Outer05();outer05.f1();//外部其他类---不能访问----->匿名内部类System.out.println("main outer05 hashcode=" + outer05);}
}class Outer05 {private int n1 = 99;public void f1() {//创建一个基于类的匿名内部类//不能添加访问修饰符,因为它的地位就是一个局部变量//作用域 : 仅仅在定义它的方法或代码块中Person p = new Person(){private int n1 = 88;@Overridepublic void hi() {// 可以直接访问外部类的所有成员,包含私有的// 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,// 默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +" 外部内的n1=" + Outer05.this.n1 );// Outer05.this 就是调用 f1的 对象System.out.println("Outer05.this hashcode=" + Outer05.this);}};p.hi();//动态绑定, 运行类型是 Outer05$1//也可以直接调用, 匿名内部类本身也是返回对象// class 匿名内部类 extends Person {}
//        new Person(){
//            @Override
//            public void hi() {
//                System.out.println("匿名内部类重写了 hi方法,哈哈...");
//            }
//            @Override
//            public void ok(String str) {
//                super.ok(str);
//            }
//        }.ok("jack");}
}class Person {//类public void hi() {System.out.println("Person hi()");}public void ok(String str) {System.out.println("Person ok() " + str);}
}
//抽象类/接口...

29.6匿名内部类的最佳实践

当做实参直接传递,简洁高效。

package com.hspedu.innerclass;import com.hspedu.abstract_.AA;public class InnerClassExercise01 {public static void main(String[] args) {//当做实参直接传递,简洁高效f1(new IL() {@Overridepublic void show() {System.out.println("这是一副名画~~...");}});//传统方法f1(new Picture());}//静态方法,形参是接口类型public static void f1(IL il) {il.show();}
}//接口
interface IL {void show();
}//类->实现IL => 编程领域 (硬编码)
class Picture implements IL {@Overridepublic void show() {System.out.println("这是一副名画XX...");}
}

有一个铃声接口Bell,里面有个ring方法。有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型。测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了。再传入另一个匿名内部类(对象),打印:小伙伴上课了

package com.hspedu.innerclass;public class InnerClassExercise02 {public static void main(String[] args) {/*1.有一个铃声接口Bell,里面有个ring方法。(右图)2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型(右图)3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了4.再传入另一个匿名内部类(对象),打印:小伙伴上课了*/CellPhone cellPhone = new CellPhone();//老韩解读//1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1//2. 重写了 ring//3. Bell bell = new Bell() {//            @Override//            public void ring() {//                System.out.println("懒猪起床了");//            }//        }cellPhone.alarmClock(new Bell() {@Overridepublic void ring() {System.out.println("懒猪起床了");}});cellPhone.alarmClock(new Bell() {@Overridepublic void ring() {System.out.println("小伙伴上课了");}});}
}
interface Bell{ //接口void ring();//方法
}
class CellPhone{//类public void alarmClock(Bell bell){//形参是Bell接口类型System.out.println(bell.getClass());bell.ring();//动态绑定}
}

29.7成员内部类的使用

说明: 成员内部类是定义在外部类的成员位置,并且没有static修饰。

1.可以直接访问外部类的所有成员,包含私有的。

2.可以添加任意访问修饰符(public、protected、默认、private), 因为它的地
位就是一个成员。

3.作用域和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法。

4.成员内部类—访问---->外部类成员(比如:属性) 访问方式:直接访问

5.外部类—访问------>成员内部类(说明) 访问方式:创建对象,再访问

6.外部其他类—访问---->成员内部类

7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

package com.hspedu.innerclass;public class MemberInnerClass01 {public static void main(String[] args) {Outer08 outer08 = new Outer08();outer08.t1();//外部其他类,使用成员内部类的三种方式// 第一种方式// outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员// 这就是一个语法,不要特别的纠结.Outer08.Inner08 inner08 = outer08.new Inner08();inner08.say();// 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象Outer08.Inner08 inner08Instance = outer08.getInner08Instance();inner08Instance.say();}
}class Outer08 { //外部类private int n1 = 10;public String name = "张三";private void hi() {System.out.println("hi()方法...");}//1.注意: 成员内部类,是定义在外部内的成员位置上//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员public class Inner08 {//成员内部类private double sal = 99.8;private int n1 = 66;public void say() {//可以直接访问外部类的所有成员,包含私有的//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.//,可以通过  外部类名.this.属性 来访问外部类的成员System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1);hi();}}//方法,返回一个Inner08实例public Inner08 getInner08Instance(){return new Inner08();}//写方法public void t1() {//使用成员内部类//创建成员内部类的对象,然后使用相关的方法Inner08 inner08 = new Inner08();inner08.say();System.out.println(inner08.sal);}
}

29.8静态内部类的使用

说明:静态内部类是定义在外部类的成员位置, 并且有static修饰

1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。

2.可以添加任意访问修饰符(public. protected、默认、private),因为它的地位就是一个成员。

3.作用域:同其他的成员,为整个类体。

4.静态内部类—访问---->外部类(比如:静态属性)[访问方式:直接访问所有静态成员]。

5.外部类—访问------>静态内部类 访问方式:创建对象,再访问。

6.外部其他类—访问----->静态内部类。

7.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访向。

package com.hspedu.innerclass;public class StaticInnerClass01 {public static void main(String[] args) {Outer10 outer10 = new Outer10();outer10.m1();//外部其他类 使用静态内部类//方式1//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)Outer10.Inner10 inner10 = new Outer10.Inner10();inner10.say();//方式2//编写一个方法,可以返回静态内部类的对象实例.Outer10.Inner10 inner101 = outer10.getInner10();System.out.println("============");inner101.say();Outer10.Inner10 inner10_ = Outer10.getInner10_();System.out.println("************");inner10_.say();}
}class Outer10 { //外部类private int n1 = 10;private static String name = "张三";private static void cry() {}//Inner10就是静态内部类//1. 放在外部类的成员位置//2. 使用static 修饰//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员//5. 作用域 :同其他的成员,为整个类体static class Inner10 {private static String name = "Timerring";public void say() {//如果外部类和静态内部类的成员重名时,静态内部类访问的时,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)System.out.println(name + " 外部类name= " + Outer10.name);cry();}}public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问Inner10 inner10 = new Inner10();inner10.say();}public Inner10 getInner10() {return new Inner10();}public static Inner10 getInner10_() {return new Inner10();}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com