韩顺平Java学习笔记(三)自用

  •  包

包的三大作用

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类
  3. 控制访问范围

包基本语法

package com.hspedu

说明:

  1. package关键字,表示打包
  2. com.hspedu表示包名

包的本质分析

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

包的命名

命名规则

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

命名规范

一般是小写字母+小圆点  一般是com.公司名.项目名.业务模块名

常用的包

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

如何引入包

语法:import 包;

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

注意事项和使用细节

  1. package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
  2. import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求

访问修饰符

基本介绍

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

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

四种访问修饰符的访问范围

访问级别访问控制修饰符同类同包子类不同包
1公开public
2受保护protected×
3默认没有修饰符××
4私有private×××

使用的注意事项

  1. 修饰符可以用来修饰类中的属性、成员方法以及类
  2. 只有默认和public才能修饰类,并且遵循上述访问权限的特点
  3. 成员方法的访问规则和属性完全一样

面向对象编程三大特征

基本介绍

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

封装

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

封装的理解和好处

  1. 隐藏实现细节:方法(连接数据库)<--调用(传入参数)
  2. 可以对数据进行验证,保证安全合理

封装的实现步骤

  • 将属性进行私有化private【不能直接修改属性】
  • 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//Xxx表示某个属性
    //加入数据验证的业务逻辑
    属性 = 参数名;
}
  • 提供一个公共的(public)put方法,用于获取属性的值
public void putXxx(类型 参数名){//权限判断Xxx某个属性
    return xx;
}

继承

为什么需要继承:两个类的属性和方法有很多相同=>继承(代码复用性)

继承基本介绍和示意图

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

继承的基本语法

class 子类 extends 父类{

}

  1. 子类就会自动拥有夫类定义的属性和方法
  2. 父类又叫超类、基类
  3. 子类又叫派生类

继承给编程带来的便利

  1. 代码复用性提高了
  2. 代码的可扩展性和维护性提高了

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

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类中直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问
  2. 子类必须调用父类的构造器,完成父类的初始化
  3. 当创建子类时,不管使用子类哪个构造器,默认情况下总会全部调用父类的无参构造器,如果父类没有无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
  5. super在使用时,必须放在构造器第一行(super只能在构造器中使用)
  6. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  7. Java所有类都是Object类的子类,Object是所有类的基类
  8. 父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)
  9. 子类最多只能继承一个父类(指直接继承),即Java中是单继承机制
  10. 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系

继承的本质分析

super关键字

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

基本语法

  1. 访问父类的属性,但不能访问父类的private属性:super.属性名;
  2. 访问父类的方法,不能访问父类的private方法:super.方法名(参数列表);
  3. 访问父类的构造器:super(参数列表);  只能放在构造器的第一句,只能出现一句!

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

  1. 调用父类的构造器的好处:分工明确,父类属性有父类初始化,子类的属性由子类初始化
  2. 当子类中有和父类中的成员(属性或方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果
  3. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果一个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C,当然也遵守访问权限的相关规则

super和this的比较

区别点thissuper
访问属性访问本类中的属性,如果本类中没有此属性则从父类中继续查找从父类开始查找属性
调用方法访问本类中的方法,如果本类中没有此属性则从父类中继续查找从父类开始查找方法
调用构造器调用本类构造器,必须放在构造器的首行调用父类的构造器,必须放在子类构造器的首行
特殊表示当前对象子类中访问父类对象

方法重写/覆盖(Override)

子类有一个方法和父类的某个方法的名称、返回类型、参数一样,那么就说子类的这个方法覆盖了父类的方法

注意事项和使用细节

方法重写也叫方法覆盖,需要满足以下条件:

  1. 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或是父类返回类型的子类。比如:父类返回类型是Object,子类返回类型是String
  3. 子类方法不能缩小父类方法的访问权限 public>protected>默认>private

方法重写和重载的比较

名称发生范围方法名行参列表返回类型修饰符
重载(Overload)本类必须一样类型,个数或者顺序至少有一个不同无要求无要求
重写(Override)父子类必须一样相同子类重写的方法,返回的类型和父类返回的类型一致,或是其子类子类方法不能缩父类方法的访问范围

多态

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

多态的具体体现

  • 方法的多态:重写和重载就体现多态
  • 对象的多态
    • 一个对象的编译类型和运行类型可以不一致
    • 编译类型在定义对象时就确定了,不能改变
    • 运行类型是可以变化的
    • 编译类型看定义时等号的左边,运行类型看等好的右边

注意事项和细节讨论

  • 多态的前提是:两个对象(类)存在继承关系
  • 多态的向上转型
    • 本质:父类的引用指向了子类的对象
    • 语法:父类类型 引用名 = new 子类类型( );
    • 特点:
      • 编译类型看左边,运行类型看右边;
      • 可以调用父类中的所有成员(需遵循访问权限);
      • 不能调用子类中的特有成员;
      • 最终运行效果看子类的具体实现。
  • 多态向下转型
    • 语法:子类类型 引用名 = (子类类型) 父类引用;
    • 只能强转父类的引用,不能强转父类的对象
    • 要求父类的引用必须指向的是当前目标类型的对象
    • 当向下转型后,可以调用子类类型中的所有成员
//添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法
public void testWork(Employee e) {
    if(e instanceof Worker) {
        ((Worker) e).work();//有向下转型操作
    } else if(e instanceof Manager) {
        ((Manager) e).manage();//有向下转型操作
    } else {
        System.out.println("不做处理...");
    }
}
  • 属性没有重写之说!属性的值看编译类型
  • instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

Java的动态绑定机制(非常非常重要)

        若把B类中的sum()方法注释掉,在执行System.out.println(a.sum());,调用的是A类中的sum()方法,但是由于动态绑定机制,该方法中的getI()方法调用的是B类中的getI()方法,所以输出的是30。

        若把B类中的sum1()方法注释掉,在执行System.out.println(a.sum1());,调用的是A类中的sum1()方法,但是由于该方法中调用的是对象属性,所以i为10,最终输出的是20。

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

多态的应用

1) 多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

//应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、
// 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中
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);

2) 多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型

Object类详解

equals方法

== 和 equals的对比

  • == 是一个比较运算符
    • 及可以判断基本类型,又可以判断引用类型
    • 如果判断基本类型,判断的是值是否相等
    • 如果判断引用类型,判断的是地址是否相等
  • equals是Object类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String

如何重写equals方法

应用实例:判断两个Person对象的内容分是否相等

class Person{ //extends Object
    private String name;
    private int age;
    private char gender;
    //重写 Object 的 equals 方法
    public boolean equals(Object obj) {
    //判断如果比较的两个对象是同一个对象,则直接返回 true
    if(this == obj) {
        return true;
    }
    //类型判断
    if(obj instanceof Person) {//是 Person才比较
        //进行 向下转型, 因为我需要得到 obj 的 各个属性
        Person p = (Person)obj;
        return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
    }
    //如果不是 Person ,则直接返回 false
    return false;
}

hashCode方法

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

hashCode的常规协定是:

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

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

小结:

  1. 提高具有哈希结构的容器的效率!
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址
  5. 后面在集合,中 hashCode 如果需要的话,也会重写。

toString方法

默认返回:全类名 +@+ 哈希值的十六进制,【查看 Object toString 方法】
  • 子类往往重写 toString 方法,用于返回对象的属性信息
  • 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式
  • 当直接输出一个对象时, toString 方法会被默认的调用 , 比如 System.out.println(monster); monster.toString()

重写toString方法

public class ToString_ {
    public static void main(String[] args) {
    /*
    Object 的 toString() 源码
    (1)getClass().getName() 类的全类名(包名+类名 )
    (2)Integer.toHexString(hashCode()) 将对象的 hashCode 值转成 16 进制字符串
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    */

    Monster monster = new Monster("小妖怪", "巡山的", 1000);
    System.out.println(monster.toString() + " hashcode=" + monster.hashCode());

    System.out.println("==当直接输出一个对象时,toString 方法会被默认的调用==");
    System.out.println(monster); //等价 monster.toString()
    }
}

class Monster {
    private String name;
    private String job;
    private double sal;
    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }
    //重写 toString 方法, 输出对象的属性
    //使用快捷键即可 alt+insert -> toString
    @Override
    public String toString() { //重写后,一般是把对象的属性值输出,当然程序员也可以自己定制
        return "Monster{" +
        "name='" + name + '\'' +
        ", job='" + job + '\'' +
        ", sal=" + sal +
        '}';
    }
    @Override
        protected void finalize() throws Throwable {
        System.out.println("fin..");
    }
}

finalize方法

  • 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作
  •  什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法。
  • 垃圾回收机制的调用,是由系统来决定( 即有自己的 GC 算法 ), 也可以通过 System.gc() 主动触发垃圾回收机制
提示: 我们在实际开发中,几乎不会运用 finalize , 所以更多就是为了应付面试
//演示 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
    @Override
    protected void finalize() throws Throwable {
        System.out.println("我们销毁 汽车" + name );
        System.out.println("释放了某些资源...");
    }
}

断点调试(debug)

  1. 断点调试是指在程序的某一行设置一个断电,调试时,程序运行到这一行就会停住,然后可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下,进行分析从而找到这个bug
  2. 断点调试也能帮助我们查看Java底层源代码的执行过程,提高程序员的Java水平

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

快捷键

  • F7:跳入方法内
  • F8: 逐行执行代码
  • shift+F8: 跳出方法
  • F9:resume,执行到下一个断点

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值