day03【多态、包、权限修饰符、内部类,Object类,Date类】
今日内容
- 多态(重点):
- 面向对象的三大特征之一:封装,继承,多态。
- 代码块
- 已经讲完了。
- 包
- 包我们介绍一下就好了
- 权限修饰符
- 介绍一下: private -> 缺省 -> protected -> public
- 内部类(很抽象)
- 知识完整性(5大成分之一)
- 只关注语法即可,实际开发几乎无用,主要是sun公司自己用,我们能理解即可!!
- 匿名内部类(重点,必须掌握的)
- Object类
- API使用工程师。90%的技术都是别人写好的.我们直接调用。
- 从这里开始,几乎没有语法了,全部是别人写好的技术我们直接调用即可,调用API。
- 都是别人做好的技术,我们拿来用:
- MySQL , JDBC, Mybatis,HTML , CSS , JS , JQuery ,UI框架 , WEB开发, Servlet , JSP
- Tomcat , Spring家族的技术(Spring , Spring MVC Spring Data JPA ) , Spring Boot
- Spring Cloud
教学目标
-
能够说出使用多态的前提条件
- ```
(1)必须有继承或者实现关系!
(2)必须存在父类类的变量引用子类类型的对象!
(3)存在方法重写!
- ```
-
理解多态的向上转型(自动类型转换)
- 自动类型转换。Animal a = new Cat();
-
理解多态的向下转型
- 强制类型转换。
- Animal a= new Cat();
- Cat c = (Cat)a;
-
能够完成笔记本案例
- 参见代码!
-
能够说出权限修饰符作用范
- private 本类
- 缺省 本类 本包其他类
- protected 本类 本包其他类 其他包下的子类中
- public 任何地方
-
说出内部类的概念
- 定义在一个类里面的类就是内部类。
-
能够说出Object类的特点
- 祖宗类,它的功能,全部类都 可以使用!!
-
能够重写Object类的toString方法
- 自动生成:重写返回对象内容输出。
-
能够重写Object类的equals方法
- 自动生成:比较对象的内容,制定比较规则。
第一章 多态[重点]
1.1 多态的形式
多态是继封装、继承之后,面向对象的第三大特性。
多态是出现在继承或者实现关系中的。
多态体现的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
多态的前提:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
总结
主程序
public class PolymorphicDemo{
public static void main(string[]args){
//父类类型对象名称=new 子类构造器;
Animal dlam=new Cat();//用父类类型的变量来接子类类型的变量
dlam.run();//调子类类型的run方法
Animal taiDi=new Dog();
taipi.run();
}
}
子类
class Dog extends Animal{
@override
public void run(){
system.out.print1n("跑的贼快~~~~!");
}
}
class cat extends Animal{
@override
public void run(){
system.out.println("暴跑的飞快~~~~!");
}
}
同一类型的对象:都是动物类;执行同一个行为(run),在不同状态下(cat 和 dog 子类不一样)会表现不同的行为特征(输出的值不同)
1.2 多态的案例演示
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。如果子类没有重写该方法,就会调用父类的该方法。
总结起来就是:编译看左边,运行看右边。
代码如下:
定义父类:
public class Animal {
public void eat(){
System.out.println("动物吃东西!")
}
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Animal a1 = new Cat();
// 调用的是 Cat 的 eat
a1.eat();
// 多态形式,创建对象
Animal a2 = new Dog();
// 调用的是 Dog 的 eat
a2.eat();
}
}
总结
编译看等号左边,是否有相应的代码,运行看等号右边,运行时是运行等号右边调用的代码
变量声明为父类类型,而变量是子类对象
变量没有多态的概念,所以编译看左边,运行也看左边
目标:多态的入门概述。
面向对象的三大特征:封装,继承,多态。
多态的形式:
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
父类类型的范围 > 子类类型范围的。
多态的概念:
同一个类型的对象,执行同一个行为,在不同的状态下会表现出不同的行为特征。
多态的识别技巧:
对于方法的调用:编译看左边,运行看右边。
对于变量的调用:编译看左边,运行看左边。
多态的使用前提:
(1) 必须存在继承或者实现关系。
(2) 必须存在父类类型的变量引用子类类型的对象。
(3) 需要存在方法重写。
小结:
记住多态的形式,识别,概念等语法即可!
1.3 多态的定义和前提
多态: 是指同一行为,具有多个不同表现形式。
从上面案例可以看出,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。
前提【重点】
-
继承或者实现【二选一】
-
方法的重写【意义体现:不重写,无意义】
-
父类引用指向子类对象【格式体现】
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
1.4 多态的好处
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。代码如下:
定义父类:
public abstract class Animal {
public abstract void eat();
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。
不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用showAnimalEat都可以完成。从而实现了实现类的自动切换。
所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。
1.5 多态的弊端
我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了。
class Animal{
public void eat(){
System.out.println("动物吃东西!")
}
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
class Test{
public static void main(String[] args){
Animal a = new Cat();
a.eat();
a.catchMouse();//编译报错,编译看左边,Animal没有这个方法
}
}
总结
目标:多态的优劣势。
优势:
1.在多态形式下,右边对象可以实现组件化切换,业务功能也随之改变,
便于扩展和维护。可以实现类与类之间的解耦。
2.实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,
可以传入一切子类对象进行方法的调用,更能体现出多态的扩展性与便利。
劣势:
1.多态形式下,不能直接调用子类特有的功能。编译看左边!! 左边
父类中没有子类独有的功能,所以代码在编译阶段就直接报错了!
小结:
记住以上语法!
1.6 引用类型转换
1.6.1 为什么要转型
多态的写法就无法访问子类独有功能了。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
回顾基本数据类型转换
- 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
- 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
总结
/**
目标:引用数据类型的自动类型转换。
在基础班学过了基本数据类型的转换。
1.小范围类型的变量或者值可以直接赋值给大范围类型的变量。
2.大范围类型的变量或者值必须强制类型转换给小范围类型的变量。
引用数据类型转换的思想是一样的:
父类类型的范围 > 子类类型的范围。
Animal Cat
引用数据类型的自动类型转换语法:
1.子类类型的对象或者变量可以自动类型转换赋值给父类类型的变量。
小结:
记住语法!
引用类型的自动类型转换并不能解决多态的劣势。
*/
public class PolymorphicDemo {
public static void main(String[] args) {
// 1.引用类型的自动类型转换:小范围的对象赋值给大范围的变量
Animal a = new Cat();
// 2.引用类型的自动类型转换:小范围的变量赋值给大范围的变量
Cat c = new Cat();
Animal a1 = c;
}
}
1.6.2 向上转型(自动转换)
- 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
**原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。**所以子类范围小可以直接自动转型给父类类型的变量。
1.6.3 向下转型(强制转换)
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
总结
大范围转为小范围需要强制转换:小范围=大范围(从右往左读),按ait+enter,选择第一个能够自动强制转换
判断正确返回为true就能执行下一步的操作
目标:引用类型强制类型转换
引用类型强制类型转换的语法:
1.父类类型的变量或者对象必须强制类型转换成子类类型的变量,否则报错!
强制类型转换的格式:
类型 变量名称 = (类型)(对象或者变量)
注意:有继承/实现关系的两个类型就可以进行强制类型转换,编译阶段一定不报错!
但是运行阶段可能出现:类型转换异常 ClassCastException
Java建议在进行强制类型转换之前先判断变量的真实类型,再强制类型转换!
变量 instanceof 类型: 判断前面的变量是否是后面的类型或者其子类类型才会返回true,
真实类型看右边new的类型,强制转换的时候类型看左边父类定义变量时的父类类型
1.6.4 案例演示
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
转型演示,代码如下:
定义类:
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void watchHouse() {
System.out.println("看家");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
总结
多态中,子类有继承父类,并且多个子类都有相同的方法,就能用父类的对象来进行调用
1.6.5 转型的异常
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。
1.6.6 instanceof关键字
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
所以,转换前,我们最好先做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
第二章 内部类
2.1 概述
2.1.1 什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
内部类是Java类的五大成份之一,也是我们最后一个需要学习的成份。
总结
成员变量是属性和信息;方法是行为;构造器是初始化对象;代码块是类加载时执行(静态代码块)或者,对象加载后执行(实例代码块),做资源初始化操作
private不能修饰外部类,缺省只能在本包下访问
2.1.2 什么时候使用内部类
一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用
- 人里面有一颗心脏。
- 汽车内部有一个发动机。
- 为了实现更好的封装性。
2.2 内部类的分类
按定义的位置来分
- 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
- 实例内部内,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
- 局部内部类,类定义在方法内
- 匿名内部类。一般定义在方法中,或者可执行代码中
2.3 静态内部类
静态内部类特点:
- 有static修饰的内部类,属于外部类本身的。
- 总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。
- 拓展:静态内部类可以直接访问外部类的静态成员。
内部类的使用格式:
外部类.内部类。
静态内部类对象的创建格式:
外部类.内部类 变量 = new 外部类.内部类构造器;
案例演示:
// 外部类:Outer01
class Outer01{
private static String sc_name = "黑马程序";
// 内部类: Inner01
public static class Inner01{
// 这里面的东西与类是完全一样的。
private String name;
public Inner01(String name) {
this.name = name;
}
public void showName(){
System.out.println(this.name);
// 拓展:静态内部类可以直接访问外部类的静态成员。
System.out.println(sc_name);
}
}
}
public class InnerClassDemo01 {
public static void main(String[] args) {
// 创建静态内部类对象。
// 外部类.内部类 变量 = new 外部类.内部类构造器;
Outer01.Inner01 in = new Outer01.Inner01("张三");
in.showName();
}
}
总结
Inner in = new Inner(); inner()就是内部类的无参构造器
同一个类中,用类名访问变量,类名.变量。通常类名可以省略
实例成员不属于外部类本身;静态内部类属于外部类本身,外部类的静态变量也属于外部类本身,外部类的实例成员属于对象,故静态内部类不直接访问实例外部类,但可以通过在内部类创建外部类对象来进行访问
目标:静态内部类的研究(了解语法即可)
什么是静态内部类?
有static修饰,属于外部类本身,会加载一次。
静态内部类中的成分研究:
类有的成分它都有,静态内部类属于外部类本身,只会加载一次
所以它的特点与外部类是完全一样的,只是位置在别人里面而已。
外部类=宿主
内部类=寄生
静态内部类的访问格式:
外部类名称.内部类名称
静态内部类创建对象的格式:
外部类名称.内部类名称 对象名称 = new 外部类名称.内部类构造器;
静态内部类的访问拓展:
静态内部类中是否可以直接访问外部类的静态成员?可以的,外部类的静态成员只有一份,可以被共享!
静态内部类中是否可以直接访问外部类的实例成员?不可以的,外部类的是成员必须用外部类对象访问!!
小结:
静态内部类属于外部类本身,只会加载一次
所以它的特点与外部类是完全一样的,只是位置在别人里面而已。
2.4 实例内部类
实例内部类特点:
- 无static修饰的内部类,属于外部类对象的。
- 宿主:外部类对象。
内部类的使用格式:
外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
实例内部类创建对象格式:
外部类.内部类 变量 = new 外部类构造器.new 内部类构造器;
-
拓展1:实例内部类不能定义静态成员。
-
拓展2:实例内部类可以直接访问外部类的私有和静态成员。
案例演示
public class InnerClassDemo02 {
public static void main(String[] args) {
// 宿主:外部类对象。
// Outer02 out = new Outer02();
// 创建内部类对象。
Outer02.Inner02 in = new Outer02().new Inner02("张三");
in.showName();
}
}
class Outer02{
// 实例内部类,属于外部类对象的。
// 拓展:实例内部类不能定义静态成员。
public class Inner02{
// 这里面的东西与类是完全一样的。
private String name;
public Inner02(String name) {
this.name = name;
}
public void showName(){
System.out.println(this.name);
}
}
}
总结
get 与set 方法属于实例方法
实例内部类可以定义实例变量和实例方法
用反证法理解:实例内部类不能定义静态成员;静态成员包括静态变量和静态方法,静态成员只能加载一次,假设实例内部类可以加载静态成员,实例内部类属于外部对象,如果外部类对象有一千个,而实例内部类会加载一千次,里面的静态成员也要跟着内部类加载一千次,而静态成员只能加载一次,矛盾了,所以就禁止在实例内部类定义静态成员;而常量固定只有一份,不会岁实例内部类的加载而加载,故可以定义常量
实例内部类的创建格式:先用外部类构造器创建外部类对象,再用外部类对象来new内部类对象
理解实例内部类可以访问外部类的实例成员 :实例内部类属于外部类对象,外部类对象属于外部类对象,故实例内部类的所有东西都可以访问外部类对象
目标:内部类_实例内部类(成员内部类)(了解语法为主)
什么是实例内部类:
无static修饰的内部类,属于外部类的每个对象的,跟着对象一起加载的。
实例内部类的成分特点:
实例内部类中不能定义静态成员,其他都可以定义。
可以定义常量。
实例内部类的访问格式:
外部类名称.内部类名称。
创建对象的格式:
外部类名称.内部类名称 对象名称 = new 外部类构造器.new 内部构造器;
拓展:
实例内部类中是否可以直接访问外部类的静态成员?可以的,外部类的静态成员可以被共享访问!
实例内部类中是否可以访问外部类的实例成员?可以的,实例内部类属于外部类对象,可以直接访问当前外部类对象的实例成员!
小结:
实例内部类属于外部类对象,需要用外部类对象一起加载,
实例内部类可以访问外部类的全部成员!
2.5 实例内部类面试题
请在?地方向上相应代码,以达到输出的内容
注意:内部类访问外部类对象的格式是:外部类名.this
public class Demo05 {
public static void main(String[] args) {
Body.Heart heart = new Body().new Heart();
heart.jump();
}
}
class Body { // 身体
private int weight = 30;
// 在成员位置定义一个类
class Heart {
private int weight = 20;
public void jump() {
int weight = 10;
System.out.println("心脏在跳动 " + ?); // 10
System.out.println("心脏在跳动 " + ?); // 20
System.out.println("心脏在跳动 " + ?); // 30
}
}
}
2.6 局部内部类
- 局部内部类 :定义在方法中的类。
定义格式:
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
// …
class 内部类 {
// 成员变量
// 成员方法
}
}
}
局部内部类编译后仍然是一个独立的类,编译后有$还有一个数字。Chinese$1Chopsticks.class
总结
成员变量不能定义在方法里
目标:局部内部类。(几乎不用)
定义在方法中,在构造器中,代码块中,for循环中定义的内部类
就是局部内部类。
局部内部类中的成分特点:
只能定义实例成员,不能定义静态成员
可以定义常量的。
小结:
局部内部类没啥用。
2.7 匿名内部类【重点】
2.7.1 概述
匿名内部类 :是内部类的简化写法。它的本质是一个带具体实现的
父类或者父接口的
匿名的
子类对象。
开发中,最常用到的内部类就是匿名内部类了。
2.7.2 引入
实际上,如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用
是为了简化代码。
之前我們使用接口时,似乎得做如下几步操作:
- 定义子类
- 重写接口中的方法
- 创建子类对象
- 调用重写后的方法
interface Swim {
public abstract void swimming();
}
// 1. 定义接口的实现类
class Student implements Swim {
// 2. 重写抽象方法
@Override
public void swimming() {
System.out.println("狗刨式...");
}
}
public class Demo07 {
public static void main(String[] args) {
// 3. 创建实现类对象
Student s = new Student();
// 4. 调用方法
s.swimming();
}
}
我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。
2.7.3 匿名内部类前提和格式
匿名内部类必须继承一个父类或者实现一个父接口。
匿名内部类格式
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
2.7.4 使用方式
以接口为例,匿名内部类的使用,代码如下:
创建匿名内部类,并调用:GUI做界面
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 使用匿名内部类
new Swim() {
@Override
public void swimming() {
System.out.println("自由泳...");
}
}.swimming();
// 接口 变量 = new 实现类(); // 多态,走子类的重写方法
Swim s2 = new Swim() {
@Override
public void swimming() {
System.out.println("蛙泳...");
}
};
s2.swimming();
s2.swimming();
}
}
总结
用接口直接new对象,然后重写接口的接口方法
对象回调,对象传递值,然后反过来调用对象的方法
可以直接省略创建变量,直接new对象,作为方法的参数(方法的入参);整个匿名内部类创建的对象作为参数进行传递
2.7.5 匿名内部类的特点
- 定义一个没有名字的内部类
- 这个类实现了父类,或者父类接口
- 匿名内部类会创建这个没有名字的类的对象
2.7.6 匿名内部类的使用场景
通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 普通方式传入对象
// 创建实现类对象
Student s = new Student();
goSwimming(s);
// 匿名内部类使用场景:作为方法参数传递
Swim s3 = new Swim() {
@Override
public void swimming() {
System.out.println("蝶泳...");
}
};
// 传入匿名内部类
goSwimming(s3);
// 完美方案: 一步到位
goSwimming(new Swim() {
public void swimming() {
System.out.println("大学生, 蛙泳...");
}
});
goSwimming(new Swim() {
public void swimming() {
System.out.println("小学生, 自由泳...");
}
});
}
// 定义一个方法,模拟请一些人去游泳
public static void goSwimming(Swim s) {
s.swimming();
}
}
总结
为按钮设置单击事件监听器,查看单击事件监听器的源码可得,参数需要一个接口,而接口创建一个接口的匿名内部类
第三章 包和权限修饰符
3.1 包
包我们每天建的项目就是在一个目录下,我们每次都会建立一个包,这个包在磁盘下其实就是一个目录。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。
在IDEA项目中,建包的操作如下:
包名的命名规范:
路径名.路径名.xxx.xxx
// 例如:com.itheima.oa
- 包名一般是公司域名的倒写。例如:黑马是www.itheima.com,包名就可以定义成com.itheima.技术名称。
- 包名必须用”.“连接。
- 包名的每个路径名必须是一个合法的标识符,而且不能是Java的关键字。
总结
必须在类名的最上面
注意:
相同包下的类可以直接访问。
不同包下的类必须导包,才可以使用!
导包格式:import 包名.类名;
// 相同包下的类可以直接访问。
// 不同包下的类必须先导包才可以使用!
3.2 权限修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和缺省(default默认)修饰符的作用。
- public:公共的,所有地方都可以访问。
- protected:当前类 ,当前包,当前类的子类可以访问。
- 缺省(没有修饰符):当前类 ,当前包可以访问。
- private:私有的,当前类可以访问。
public > protected > 缺省 > private
总结
当前类的子类:具有继承关系,例public class Zi extends Fu,子类就能访问父类的protected修饰的成员
3.3 不同权限的访问能力
public | protected | 缺省(空的) | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用
private
,隐藏细节。 - 构造方法使用
public
,方便创建对象。 - 成员方法使用
public
,方便调用方法。
小贴士:不加权限修饰符,就是default权限
第四章 Object类
4.1 概述
java.lang.Object
类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。
如果一个类没有特别指定父类, 那么默认则继承自Object类。例如:
public class MyClass /*extends Object*/ {
// ...
}
根据JDK源代码及Object类的API文档,Object类当中包含的方法有11个。今天我们主要学习其中的2个:
public String toString()
:返回该对象的字符串表示。public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。
4.2 toString方法
方法摘要
public String toString()
:返回该对象的字符串表示。
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
覆盖重写
如果不希望使用toString方法的默认行为,则可以对它进行覆盖重写。例如自定义的Person类:
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 省略构造器与Getter Setter
}
在IntelliJ IDEA中,可以点击Code
菜单中的Generate...
,也可以使用快捷键alt+insert
,点击toString()
选项。选择需要包含的成员变量并确定。如下图所示:
小贴士: 在我们直接使用输出语句输出对象名的时候,其实通过该对象调用了其toString()方法。
总结
因为所有的类默认继承object ,所有就有toString的方法
在ide右键生成选择toString可以自动重写子类的toString方法
目标:常用API的学习-Object类的toString方法使用详解。
引入
包:java.lang.Object
Object类是Java中的祖宗类。
一个类要么默认继承了Object类,要么间接继承了Object类。
Object类的方法是一切子类都可以直接使用的,所以我们要学习Object类的方法。
Object类的常用方法:
(1)public String toString():
– 默认是返回当前对象在堆内存中的地址信息:
com.itheima._12Object类的详细使用.Student@735b478
– 默认的地址信息格式:类的全限名@内存地址
– 直接输出对象名称,默认会自动调用toString()方法,所以输出对象toString()调用可以省略不写
– 开发中直接输出对象,默认输出对象的地址其实是毫无意义的。
开发中输出对象变量,更多的时候是希望看到对象的内容数据而不是对象的地址信息!
所以父类toString()方法存在的意义就是为了被子类重写,以便
返回对象的内容信息输出!!
小结:
toString()默认是返回当前对象在堆内存中的地址信息:
开发中输出对象变量,更多的时候是希望看到对象的内容数据而不是对象的地址信息!
所以父类toString()方法存在的意义就是为了被子类重写,重写toString可以看到对象的内容信息。
重写Object的toString()以便返回对象的内容数据
谁调用this就指谁,例 zs1.equals(zs2),是zs1调用this ,this就指zs1
基础类型用==来进行比较,string类型用equals来进行比较
4.3 equals方法
方法摘要
public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。
调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”有默认和自定义两种方式。
默认地址比较
如果没有覆盖重写equals方法,那么Object类中默认进行==
运算符的对象地址比较,只要不是同一个对象,结果必然为false。
对象内容比较
如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。例如:
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 如果对象地址一样,则认为相同
if (this == o)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
if (o == null || getClass() != o.getClass())
return false;
// 转换为当前类型
Person person = (Person) o;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
}
这段代码充分考虑了对象为空、类型一致等问题,但方法内容并不唯一。大多数IDE都可以自动生成equals方法的代码内容。在IntelliJ IDEA中,可以使用Code
菜单中的Generate…
选项,也可以使用快捷键alt+insert
,并选择equals() and hashCode()
进行自动代码生成。如下图所示:
tips:Object类当中的hashCode等其他方法,今后学习。
总结
if(this. name. equals(zs2. name)&& this. age==zs2. age
&& this. sex==zs2. sex){
return true;
上面的代码可以简化为下面的代码
return this. name. equals(zs2. name)&& this. age==zs2. age
&& this. sex==zs2. sex);
用this.xxx可以调用引用类型
(2)public boolean equals(Object o):
– 默认是比较两个对象的地址是否相同。相同返回true,反之。
– 直接比较两个对象的地址是否相同完全可以用“==”替代equals。
所以equals存在的意义是为了被子类重写,以便程序员可以
自己来定制比较规则。
– 需求:只要两个对象的内容一样,我们就认为他们是相等的。
小结:
equals存在的意义是为了被子类重写,以便程序员可以
自己来定制比较规则。
第五章 Objects类
Objects类是对象工具类,它里面的的方法都是用来操作对象的。
5.1 equals方法
在刚才IDEA自动重写equals代码中,使用到了java.util.Objects
类,那么这个类是什么呢?
在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法如下:
public static boolean equals(Object a, Object b)
:判断两个对象是否相等。
我们可以查看一下源码,学习一下:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
5.2 isNull
static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true。
Student s1 = null;
Student s2 = new Student("蔡徐坤", 22);
// static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true
System.out.println(Objects.isNull(s1)); // true
System.out.println(Objects.isNull(s2)); // false
总结
在idea 菜单栏中的build 中build Module 重新编译模块可以解决底层无法识别代码的情况
用空来调用方法就会出现空指针异常
is null相当于 ==null
目标:Objects类的使用。
Objects类与Object还是继承关系。
Objects类是从JDK 1.7开始之后才有的。
Objects的方法:
1.public static boolean equals(Object a, Object b)
-- 比较两个对象的。
-- 底层进行非空判断,从而可以避免空指针异常。更安全!!推荐使用!!
public static boolean equals(Object a, Object b) {
return a == b || a != null && a.equals(b);
}
2.public static boolean isNull(Object obj)
-- 判断变量是否为null ,为null返回true ,反之!
第六章 Date类
6.1 Date概述
java.util.Date`类 表示特定的瞬间,精确到毫秒。
继续查阅Date类的描述,发现Date拥有多个构造函数,只是部分已经过时,我们重点看以下两个构造函数
public Date()
:从运行程序的此时此刻到时间原点经历的毫秒值,转换成Date对象,分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。public Date(long date)
:将指定参数的毫秒值date,转换成Date对象,分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。
tips: 由于中国处于东八区(GMT+08:00)是比世界协调时间/格林尼治时间(GMT)快8小时的时区,当格林尼治标准时间为0:00时,东八区的标准时间为08:00。
简单来说:使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定long类型的构造参数,可以自定义毫秒时刻。例如:
import java.util.Date;
public class Demo01Date {
public static void main(String[] args) {
// 创建日期对象,把当前的时间
System.out.println(new Date()); // Tue Jan 16 14:37:35 CST 2020
// 创建日期对象,把当前的毫秒值转成日期对象
System.out.println(new Date(0L)); // Thu Jan 01 08:00:00 CST 1970
}
}
tips:在使用println方法时,会自动调用Date类中的toString方法。Date类对Object类中的toString方法进行了覆盖重写,所以结果为指定格式的字符串。
总结
创建对象要先看构造器
看官方的api文档只要是过时的,直接淘汰,不去学习,只要是拼出来,代码有画线删除,就是过时的代码
目标:Date日期类的使用。
Java是面向对象的,会用一个类代表一个事物。
Date类在Java中代表的是系统当前此刻日期时间对象。
Date类:
包:java.util.Date。
构造器:
-- public Date():创建当前系统的此刻日期时间对象。
-- public Date(long time):
方法:
-- public long getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来走过的总的毫秒数。
时间记录的两种方式:
a.Date日期对象。
b.时间毫秒值:从1970-01-01 00:00:00开始走到此刻的总的毫秒值。 1s = 1000ms
小结:
Date可以代表系统当前此刻日期时间对象。
时间记录的两种方式:
Date日期对象。
时间毫秒值:从1970-01-01 00:00:00开始走到此刻的总的毫秒值。 1s = 1000ms
拓展:时间毫秒值的作用。
时间毫秒值可以用于做时间的计算:例如代码的执行性能分析。
6.2 Date常用方法
Date类中的多数方法已经过时,常用的方法有:
public long getTime()
把日期对象转换成对应的时间毫秒值。public void setTime(long time)
把方法参数给定的毫秒值设置给日期对象
示例代码
public class DateDemo02 {
public static void main(String[] args) {
//创建日期对象
Date d = new Date();
//public long getTime():获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值
//System.out.println(d.getTime());
//System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");
//public void setTime(long time):设置时间,给的是毫秒值
//long time = 1000*60*60;
long time = System.currentTimeMillis();
d.setTime(time);
System.out.println(d);
}
}
小结:Date表示特定的时间瞬间,我们可以使用Date对象对时间进行操作。
总结
可以在统计时间的开始时,创建date 对象.getTime 来记录当前的时间;结束时创建date 对象.getTime 来记录当前的时间,两者相减就是经过的时间
1000毫秒等于1秒,如果要获取double类型的,就除以带.0的数值,就能显示出来小数点后面的数字(保留小数)
因为时间毫秒值是1970-1-1 0:0:0到现在,每一个毫秒值就确定一个具体的时间,所有能用具体的毫秒值来转为现在的时间
目标:Date类的有参数构造器的使用。
构造器:
-- public Date():创建当前系统的此刻日期时间对象。
-- public Date(long time):把时间毫秒值转换成日期对象。
流程:
Date日期对象 -> getTime() -> 时间毫秒值
时间毫秒值 -> new Date(时间毫秒值) -> Date日期对象
小结:
public Date(long time):把时间毫秒值转换成日期对象。