感谢尚硅谷免费提供的视频
继承
**
理解
**
生活中的继承:子女继承了父母的财产或相貌或才华
java中的继承:一个类不用自己定义属性或方法,就已经具备了另一个类的属性和方法
把这种现象称为java中的继承!
继承其他类的类,称为子类或派生类。
被继承的类,称为父类或超类或基类
好处
1、提高代码的重用性
2、提高代码的扩展性和维护性
3、为多态打下了基础
语法
使用extends关键字
class Sub extends Super{
}
使用特点
1、子类到底继承了父类的哪些成员?
所有的属性和方法,只是私有的不能直接访问,而是通过公共的方法去访问
注意:子类在访问继承来的成员时,也是需要先去遵循访问权限的限定
2、子类不能继承父类的构造器,但必须调用父类的构造器
3、不能滥用继承,子类和父类之间必须满足 is-a的关系
class 子类 extends 父类{}
子类 是父类吗?
class Book extends Animal{}
Book 是动物吗?
4、子类可以直接继承几个父类?
答:一个
java中支持单继承!
5、子类的继承不限于直接父类
6、java中所有的类都直接或间接继承了Object类,也就是Object类为所有类的祖先类
子类调用父类构造器的特点
1、子类必须调用父类的构造器
2、子类如果没有显式的调用父类的构造器,则默认调用父类的无参构造器
父类中如果没有无参构造器,则子类中必须显式的调用父类的有参构造器,通过super关键字去调用,语法为 super(实参列表);
3、构造器的调用,不限于直接父类
super关键字的使用
理解
super代表父类的引用,用于访问父类的属性、方法、构造
###语法
访问父类的属性:super.属性名=值;
访问父类的方法:super.方法(实参列表);
访问父类的构造:super(实参列表);
注意:必须在子类的构造器的第一句
注意:在相对子类中,去访问父类的成员
好处
1、当子类需要显式调用父类的构造器时,则必须通过super关键字
2、当子类和父类的成员重名时,为了访问父类的成员,则必须通过super关键字
联想:
当局部变量和当前类的成员变量重名时,可以通过this区分开
当当前类的成员变量和父类的成员变量重名时,可以通过super区分开
特点
1、super的使用不限于直接父类
2、如果父类和爷爷类中都相同的成员,则默认访问的是父类的成员,遵循就近原则
3、this(实参列表)和super(实参列表)不能同时出现在构造器中
this和super的对比
1.this:代表本类对象,可以访问本类的属性、方法、构造。它的出现主要为了区分成员变量和局部变量,以及调用本类构造器。
通过this访问成员时,先从本类中查找,如果找不到再从直接父类找,如果还找不到,继续往上追溯找间接父类,直到找到为止!
2.super:代表父类引用,可以访问父类的属性、方法、构造。它的出现主要为了区分本类和父类的成员,以及调用父类构造器。
通过super访问成员时,先从直接父类找,如果还找不到,继续往上追溯找间接父类,直到找到为止!
方法的重写
理解
子类对从父类继承过来的方法进行改造,这种现象称为方法的重写或覆写或覆盖
要求
方法名和参数列表完全相同,返回类型相同或子类重写方法的返回类型为子类类型,修饰符相同或权限更大
【经典面试题】重写和重载的对比⭐
发生范围 方法名 参数列表 返回类型 修饰符
重载 同一个类中 必须相同 必须不同 无要求 无要求
重写 父子类中 必须相同 必须相同 相同或为子类型 相同或权限更大
多态
理解
多态:一种事物有多种表现形式。面向对象的最后一个特征,继承和封装都是为多态打基础!
生活中的多态:
打招呼:
中国人:你好
英国人:hello
日本人:拷逆七娃
韩国人:阿娘哈赛you
泰国人:萨瓦迪卡
参加同学聚会:
某人:开着法拉利
某人:开着奥拓
某人:骑着摩拜
倚天屠龙记
六大门派围攻光明顶
少林:易筋经
武当:太极
峨眉:绝情剑法
崆峒:七伤拳
昆仑:
青城:
组织者:发出号令:大家开始抄起家伙围攻吧!
多态的体现
1、方法的多态性
方法的重写和重载
2、对象的多态性
对象的转型上!
Person p = new Student();
引用类型之间的类型转换(对象的多态)
前提:父子关系
一、向上转型
本质:父类的引用指向了子类的对象
语法:
父类类型 引用名 = new 子类类型();
特点:
编译看左边,运行看右边
可以调用父类类型中的所有成员,不能调用子类类型中特有成员;最终运行效果看子类的具体实现!
二、向下转型
语法:
子类类型 引用名 = (子类类型)父类引用;
特点:
①只能强转父类的引用,不能强转父类的对象
②要求父类的引用必须指向的是当前目标类型的对象
③可以调用子类类型中所有的成员
补充:属性没有重写之说!属性的值看编译类型
多态的应用
1、多态数组
数组的元素类型允许是子类类型,相当于可以容纳更多的元素
2、多态参数
实参的类型允许是子类类型,相当于提高了代码的通用性和扩展性
Object类
学习类的步骤
1、看包
2、说明【了解】
3、看构造器
4、学习里面的属性和方法
1.包
java.lang包 ***(这个LANG指的是LANGUAGE)***
注意:该包是系统默认导入的,不用手动导包
2.类的说明
Object类是所有类的超类,所有对象都具有Object类的方法
3.构造器
new Object();
注意:基本上不会创建Object对象
4.方法 ★
一、equals方法
【面试题】==和equals的对比
== 可以判断基本类型或引用类型,不管判断什么类型的元素,都要求 左右两边的类型一致或兼容,否则编译报错
如果判断的是基本类型,则判断的是值是否相等。示例:100==‘d’ true 1.0==1 true
如果判断的是引用类型,则判断的是地址是否相等。示例:person1==person2 string1 == string2
equals 只能判断引用类型,可以判断任何引用类型,编译不会报错
Object类的equals:判断的就是地址是否相等!往往子类需要重写equals方法,实现判断内容是否相等。比如:String、Integer类等都重写了equals方法
## 如何重写equals方法 ##
public boolean equals(Object obj){//this:person1 obj:person4
//步骤1:两个对象的地址一样(提高效率)
if(this==obj)
return true;
//步骤2:判断对象的类型
if(!(obj instanceof Person)){//如果传入的obj不是Person类型,则直接返回
return false;
}
//步骤3:对象的向下转型
Person p = (Person) obj;//向下转型
//步骤4:判断内容是否相等
return this.name.equals(p.name)&&this.age==p.age;
二、toString方法
1、功能
返回对象的字符串形式。默认返回的是:全类名+@+哈希值的十六进制
public String toString(){
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}
子类往往重写该方法
2、如何重写toString方法?
public String toString(){
return 属性信息;
}
3、好处:
打印方法和拼接字符串时,会自动调用对象的toString方法,提高代码的简洁性!
三、hashCode方法
1、功能
返回对象的哈希值(以整数形式返回对象的一个特殊值),
提高集合中查找元素相等的效率!
2、特点
对象的哈希值 约等于 对象的地址号
3、推论
如果哈希值不一样,则两个对象肯定不一样!
四、finalize方法
自动垃圾回收机制
原理:当应用已启动,实际上相当于开起了 主线程main和垃圾回收器线程
垃圾回收器将默默检测是否有需要回收的对象,一旦有了,则需要回收该对象以释放空间供其他有用对象使用,该对象被回收时同时调用finalize方法
1、回收哪些对象?
答:无用对象(没有任何引用指向的对象)
2、回收的时机?
答:不确定的
3、如何加速垃圾回收器的回收?
答:唤醒垃圾回收器
System.gc();
4、finalize方法什么时候被调用?
只要被回收,肯定被调用
面向对象(下)
一、static关键字
理解
static看做一种修饰符,可以用于修饰类的成员(属性、方法、内部类、初始化块),但不能修饰构造器和局部变量
使用static修饰的成员,称为静态成员
用static修饰的属性,称为静态属性
用static修饰的方法,称为静态方法
没有使用static修饰的成员,称为普通成员或实例成员
没有使用static修饰的属性,称为普通属性或实例变量
没有使用static修饰的方法,称为普通方法或实例方法
静态成员的特点
1、静态成员随着类的加载而加载
2、静态成员都是存储方法区(静态域)
3、静态成员整个内存中只有一份
4、静态成员都是和对象无关的成员,所以可以直接通过类名调用
5、静态成员(除了静态属性),里面都是可以直接调用其他静态成员,不能直接调用普通成员
单例设计模式
什么时候使用单例模式:
当一个项目中 要求某类只有一个对象时!
应用场景:
1、项目中只想多次调用该对象的方法时,为了提高效率
2、如果创建多个对象,会导致逻辑错误时
步骤:
1、构造器私有化
2、本类内部创建对象
3、提供public static方法,暴露该对象
饿汉式和懒汉式的差异:
1、创建对象的时机不同!
2、应用场合不同
如果仅仅考虑效率问题,可以使用懒汉式
如果考虑线程安全问题,可以使用饿汉式
经典案例:Runtime类就使用了饿汉式单例设计模式
【面试题】自己编写懒汉式代码!
class Single {
// ①构造器私有化
private Single() {
}
// ②本类内部声明引用
private static Single single;
// ③提供一个公共的静态方法供外部使用
public static Single getInstance() {
if (single == null) {
single = new Single();
}
return single;
}
}
//懒汉式
class Single2 {
private Single2() {
}
private static Single2 s;
//方法内部创建对象
public static Single2 getInstance() {//后创建对象
if (s == null) {
s=new Single2();
}
return s;
}
}
//单例模式:饿汉式
class Single1 {
public static int rate=100;
private Single1() {}
private static Single1 s=new Single1();//先创建对象
public static Single1 getInstance() {
return s;
}
}
三、final关键字
理解
final关键字属于一种修饰符,可以用于修饰类、属性、方法、局部变量
特点
1、修饰类
如果用final修饰类,则该类变成了“太监类”,不能被继承
代表类:String、Integer等
2、修饰方法
如果用final修饰方法,则该方法不能被重写,但可以被继承
3、修饰属性和局部变量
①如果修饰变量(属性和局部),该变量就变成了常量,一旦赋值,将不能更改! ★
②修饰属性,需要注意以下事项:
a、命名时最好遵循见名知义,且纯大写,提高代码的阅读性!
b、一般来讲static和final往往一起搭配修饰常量属性
两个修饰符的意思不冲突
调用比较方便
jvm对使用这两个修饰符搭配的属性,内部进行了优化
c、常量属性在声明时或构造器时必须初始化
③基本类型的值,一旦更改,则就是更改值,不允许!
引用类型的对象,如果更改属性值是允许的,但不能更改地址号!
四、初始化块
理解
初始化块又称为代码块。属于类中的第四大成员。本质上是一个方法,它也有方法体,但没有方法名,没有参数,没有返回,而且也不是通过对象或类名显式调用,而是通过隐式调用
语法
[修饰符]{
方法体
}
注意:
①修饰符只能是static,使用static修饰的初始化块称为静态初始化块
没有使用static修饰的初始化块称为普通初始化块
//静态初始化块
static{
}
//普通初始化块
{
}
②方法体中可以为任意逻辑语句,包含输入、输出、变量、运算等
好处
1、和构造器很像,都是用于初始化信息
2、当多个构造器中有重复的语句,可以将这些重复的语句往上提取到初始化块中,提高代码的重用性
特点
1、静态初始化块的调用时机:加载类
普通初始化块的调用时机:创建对象
2、静态初始化块只会调用一次,随着类的加载而加载,(类只加载一次)
普通初始化块可以调用多次,随着对象的创建而加载
3、一个类中可以有多个静态初始化块和多个普通初始化块
静态初始化块的执行要早于普通初始化块
同一个类型的初始化块的执行顺序取决于定义的先后顺序!
4、一个类中如果有:静态初始化块、普通初始化块、普通属性初始化、静态属性初始化、构造器
执行顺序:
静态初始化块|静态属性初始化>普通初始化块|普通属性初始化>构造器
5、有父子类
执行顺序:
爷爷类的静态初始化块|静态属性初始化>
父类静态初始化块|静态属性初始化>
子类静态初始化块|静态属性初始化>
爷爷类普通初始化块|普通属性初始化>构造器>
父类普通初始化块|普通属性初始化>构造器>
子类普通初始化块|普通属性初始化>构造器
6、静态初始化块中遵循静态成员的特点,只能直接访问静态成员!
五、抽象类和抽象方法
abstract关键字的理解
abstract关键字属于一种修饰符,用于修饰类和方法,但不能修饰属性、局部变量、构造器
使用abstract修饰的类,称为抽象类
使用abstract修饰方法,称为抽象方法
抽象类的理解和特点
理解:往往将里面包含抽象方法的父类,设计成抽象类
1、父类
2、里面有一些不好描述的行为
语法:
[访问修饰符] abstract class 类名{}
特点:
* ①抽象类不能创建本类的对象
* ②抽象类有构造器,供子类调用
* ③抽象类中可以有哪些成员?
* 任意成员(包含普通成员和抽象方法)
* ④抽象类中也可以没有抽象方法
* ⑤不能使用final修饰抽象类,因为和继承的意义相悖
* ⑥抽象类的扩展
* 普通子类 继承抽象类要求必须实现里面所有抽象方法
* 抽象子类继承抽象类不用实现里面的抽象方法
抽象方法的理解和特点
语法:
【访问修饰符】 abstract 返回类型 方法名(参数列表);
特点:
①没有方法体,只有方法签名,使用分号结尾
②修饰符不能是final、static或private中的任何一个
因为这些修饰符和重写的意义相悖
③抽象方法只能出现在抽象类或接口中!
六、接口
理解
里面有抽象方法的类,称为抽象类【半成品】
里面全是抽象方法的抽象类,称为接口。
生活的案例:
张三丰:接口
宋远桥:抽象类
太极拳:出神入化
太极剑:差一点
宋青书:具体类
接口——抽象类——具体类
一流企业卖标准,二流企业卖服务,三流企业卖产品
好处
1、避免了java中单继承的局限性
2、接口的实现更加灵活,不再是is-a的关系,而是like-a的关系
3、接口提高了类和类之间的解耦性,减少了类的依赖性
抽象类和接口的对比
1、定义关键字
抽象类:abstract class
接口: interface
2、是否有构造器
抽象类:有
接口:没有
3、是否可以创建本类对象
抽象类:不能
接口:不能
4、是否可以有普通成员
抽象类:可以有
接口:不可以,里面只能是静态常量和抽象方法(jdk8之前)
5、关于里面的抽象方法问题
抽象类:里面的抽象方法不能使用private、final、static修饰,除了这些都可以,但修饰符不可以省略
接口:里面的抽象方法只能使用public abstract修饰,而且可以省略
6、继承性
抽象类:单继承
接口:多继承多实现
7、应用
抽象类和接口都可以应用在多态上!
java面向对象继承、多态、抽象类、接口等图解
为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。提高了代码的复用性。
继承的出现让类与类之间产生了关系,可以创建更为特殊的类型。
利于可维护性。
此处的多个类抽取出来的这个类称为父类(基类或超类)。
继承父类的类可以称为子类
类继承语法规则:
class Subclass extends Superclass{ }
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
关于继承的规则:
父类中的成员,无论是公有(public)还是私有(private),均被子类继承。
子类不能对继承的私有成员直接进行访问,可通过继承的公有方法来访问。
Java只支持单继承,不允许多重继承
一个子类只能有一个父类
一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok
class SubDemo extends Demo1,Demo2...//error
调用父类的构造器
子类中所有的构造器默认都会访问父类中空参数的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造方法中调用父类的构造器
注意:
尤其当子父类出现同名成员时,可以用super进行区分
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
子类对象的实例化过程
This和super的区别
方法的重写(override)
定义:
在子类中可以根据需要对从父类中继承来的方法进行改造,也称方法的重置、覆写。在程序执行时,子类的方法将覆盖父类的方法。
要求:
重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。
重写方法不能使用比被重写方法更严格的访问权限。
重写和被重写的方法须同时为static的,或同时为非static的
子类方法抛出的异常不能大于父类被重写方法的异常
多态性,是面向对象中最重要的概念,在java中有两种体现:
方法的重载(overload)和重写(overwrite)。
对象的多态性 ——可以直接应用在抽象类和接口上★。
基本数据类型的Casting:
自动类型转换:小的数据类型可以自动转换成大的数据类型、
如long g=20; double d=12.0f
强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
如 float f=(float)12.0; int a=(int)1200L
对Java对象的强制类型转换称为造型
从子类到父类的类型转换可以自动进行
从父类到子类的类型转换必须通过造型(强制类型转换)实现
无继承关系的引用类型间的转换是非法的
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
Person p=new Student();
若编译时类型和运行时类型不一致,就出现多态(Polymorphism)
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
Person p=new Teacher();
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.score = 98; //合法,Student类有school成员变量
Person e = new Student();
e.score = 98; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
正常的方法调用(本态调用)
Person e = new Person();
e.say();
Student e = new Student();
e.say;
虚拟方法调用(多态情况下)
Person e = new Student();
e.say(); //调用Student类的say()方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的say()方法。——动态绑定
前提:
需要存在继承或者实现关系
要有覆盖操作
成员方法:
编译时:要查看引用变量所属的类中是否有所调用的方法。
运行时:调用实际对象所属的类中的重写方法。
成员变量(属性)
不具备多态性,只看引用变量所属的类。
子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
多态参数 — 方法参数列表中的引用类型参数
例如:
public static void method(Person p) {//形参
System.out.println(p.say());
}
调用此方法:
Student s = new Student();
xxx.method(s);
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
public class Person extends Object {…}
public class Student extends Person {…}
public class Graduate extends Person {…}
-------------------------------------------------------------------
public void method1(Person e) {
if (e instanceof Person)
// 处理Person类及其子类对象
if (e instanceof Student)
//处理Student类及其子类对象
if (e instanceof Graduate)
//处理Graduate类及其子类对象
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
public class Person {
...
}
等价于:
public class Person extends Object {
...
}
例:method(Object obj){…}//可以接收任何类作为其参数
Person o=new Person();
method(o);
Object类中的主要方法
= =:
基本类型比较值:只要两个变量的值相等,即为true.
int a=5; if(a==6){…}
引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true.
Person p1=new Person();
Person p2=new Person();
if (p1==p2){…}
用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错;
equals():所有类都继承了Object,也就获得了equals()方法。还可以重写。
只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
格式:obj1.equals(obj2)
特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;
原因:在这些类中重写了Object类的equals()方法。
toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。
在进行String与其它类型数据的连接操作时,自动调用toString()方法
Date now=new Date();
System.out.println(“now=”+now); 相当于
System.out.println(“now=”+now.toString());
可以根据需要在用户自定义类型中重写toString()方法
如String 类重写了toString()方法,返回字符串的值。
s1=“hello”;
System.out.println(s1);//相当于System.out.println(s1.toString());
基本类型数据转换为String类型时,调用了对应包装类的toString()方法
int a=10; System.out.println(“a=”+a);
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
class Circle{
private double radius;
public Circle(double radius){this.radius=radius;}
public double findArea(){return Math.PI*radius*radius;}}
创建两个Circle对象
Circle c1=new Circle(2.0); //c1.radius=2.0
Circle c2=new Circle(3.0); //c2.radius=3.0
Circle类中的变量radius是一个实例变量(instance variable),它属于类的每一个对象,不能被同一个类的不同对象所共享。
上例中c1的radius独立于c2的radius,存储在不同的空间。c1中的radius变化不会影响c2的radius,反之亦然。
类属性、类方法的设计思想
类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用
关键字static
使用范围:
在Java类中,可用static修饰属性、方法、代码块、内部类
被修饰后的成员具备以下特点:
随着类的加载而加载
优先于对象存在
修饰的成员,被所有对象所共享
访问权限允许时,可不创建对象,直接被类调用
案例
public class Test {
static int count = 9;
public void count() {
System.out.println("count=" + (count++));
}
public static void main(String args[]) {
new Test().count();
new Test().count();
}
}
类变量(类属性)由该类的所有实例共享
没有对象的实例时,可以用类名.方法名()的形式访问由static标记的类方法。
在static方法内部只能访问类的static属性,不能访问类的非static属性。
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
id++; //非法
return total;
}
public Person() {
total++;
id = total;
}}
public class TestPerson {
public static void main(String[] args) {
System.out.println("Number of total is " +Person.getTotalPerson());
//没有创建对象也可以访问静态方法
Person p1 = new Person();
System.out.println( "Number of total is "+ Person.getTotalPerson());
}}
因为不需要实例就可以访问static方法,因此static方法内部不能有this。(也不能有super ? YES!)
重载的方法需要同时为static的或者非static的。
class Person {
private int id;
private static int total = 0;
public static void setTotalPerson(int total){
this.total=total; //非法,在static方法中不能有this,也不能有super
}
public Person() {
total++;
id = total;
}}
public class TestPerson {
public static void main(String[] args) {
Person.setTotalPerson(3);
} }
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。”套路”
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
单例(Singleton)设计模式-饿汉式
class Single{
//private的构造器,不能在类的外部创建该类的对象
private Single() {}
//私有的,只能在类的内部访问
private static Single onlyone = new Single();
//getSingle()为static,不用创建对象即可访问
public static Single getSingle() {
return onlyone;
}
}
public class TestSingle{
public static void main(String args[]) {
Single s1 = Single.getSingle(); //访问静态方法
Single s2 = Single.getSingle();
if (s1==s2){
System.out.println("s1 is equals to s2!");
}}}
单例(Singleton)设计模式-懒汉式
class Singleton{
//1.将构造器私有化,保证在此类的外部,不能调用本类的构造器。
private Singleton(){
}
//2.先声明类的引用
//4.也需要配合static的方法,用static修饰此类的引用。
private static Singleton instance = null;
//3.设置公共的方法来访问类的实例
public static Singleton getInstance(){
//3.1如果类的实例未创建,那些先要创建,然后返回给调用者:本类。因此,需要static 修饰。
if(instance == null){
instance = new Singleton();
}
//3.2 若有了类的实例,直接返回给调用者。
return instance;
} }
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终”。
final标记的类不能被继承。提高安全性,提高程序的可读性。
String类、System类、StringBuffer类
final标记的方法不能被子类重写。
Object类中的getClass()。
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
final标记的成员变量必须在声明的同时或在每个构造方法中或代码块中显式赋值,然后才能使用。
final double PI=3.14;
final class A{
}
class B extends A{ //错误,不能被继承。
}
class A{
public final void print(){
System.out.println(“A”);
}
}
class B extends A{
public void print(){ //错误,不能被重写。
System.out.println(“尚硅谷”);
}
}
class A{
private final String INFO = “atguigu”; //声明常量
public void print(){
}
}
常量名要大写,内容不可修改。——如同古代皇帝的圣旨。
static final:全局常量
public final class Test{
public static int totalNumber = 5 ;
public final int ID;
public Test(){
ID = ++totalNumber; //可在构造方法中给final变量赋值
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.ID);
final int I = 10;
final int J;
J = 20;
J = 30; //非法
}}
由于java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是,又因为java虚拟机在执行main()方法时不必创建对象,public所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。
public class CommandPara {
public static void main(String[] args) {
for ( int i = 0; i < args.length; i++ ) {
System.out.println("args[" + i + "] = " + args[i]);
} } }
java CommandPara "lisa" "bily" "Mr Brown"
一个类中初始化块若有修饰符,则只能被static修饰,称为静态代码块(static block),当类被载入时,类属性的声明和静态代码块先后顺序被执行,且只被执行一次。
static块通常用于初始化static (类)属性
class Person {
public static int total;
static {
total = 100;//为total赋初值
}
…… //其它属性或方法声明
}
非静态代码块:没有static修饰的代码块
1.可以有输出语句。
2.可以对类的属性、类的声明进行初始化操作。
3.可以调用静态的变量或方法。
4.若有多个非静态的代码块,那么按照从上到下的顺序依
次执行。
5.每次创建对象的时候,都会执行一次。且先于构造器执行
静态代码块:用static 修饰的代码块
1.可以有输出语句。
2.可以对类的属性、类的声明进行初始化操作。
3.不可以对非静态的属性初始化。即:不可以调用非静态的属
性和方法。
4.若有多个静态的代码块,那么按照从上到下的顺序依次执行。
5.静态代码块的执行要先于非静态代码块。
6.静态代码块只执行一次
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
用abstract关键字来修饰一个类时,这个类叫做抽象类;
用abstract来修饰一个方法时,该方法叫做抽象方法。
抽象方法:只有方法的声明,没有方法的实现。以分号结束:abstract int abstractMethod( int a );
含有抽象方法的类必须被声明为抽象类。
抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。
抽象类应用
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。
模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
abstract class Template{
public final void getTime(){
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("执行时间是:"+(end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
public void code(){
for(int i = 0;i<10000;i++){
System.out.println(i);
} } }
有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
接口(interface)是抽象方法和常量值的定义的集合。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
实现接口类:
class SubClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口。
接口的特点:
用interface来定义。
接口中的所有成员变量都默认是由public static final修饰的。
接口中的所有抽象方法都默认是由public abstract修饰的。
接口中没有构造器。
接口采用多继承机制。
接口定义举例
public interface Runner {
int ID = 1;
void start();
public void run();
void stop();
}
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
定义Java类的语法格式:先写extends,后写implements
< modifier> class < name> [extends < superclass>]
[implements < interface> [,< interface>]* ] {
< declarations>*
}
public interface Runner {
public void start();
public void run();
public void stop();
}
public class Person implements Runner {
public void start() {
// 准备工作:弯腰、蹬腿、咬牙、瞪眼// 开跑
}
public void run() {
// 摆动手臂
// 维持直线方向
}
public void stop() {
// 减速直至停止、喝水。
} }
一个类可以实现多个无关的接口
interface Runner { public void run();}
interface Swimmer {public double swim();}
class Creator{public int eat(){…}}
class Man extends Creator implements Runner ,Swimmer{
public void run() {……}
public double swim() {……}
public int eat() {……}
}
与继承关系类似,接口与实现类之间存在多态性
public class Test{
public static void main(String args[]){
Test t = new Test();
Man m = new Man();
t.m1(m);
t.m2(m);
t.m3(m);
}
public String m1(Runner f) { f.run(); }
public void m2(Swimmer s) {s.swim();}
public void m3(Creator a) {a.eat();}
}
如果实现接口的类中没有实现接口中的全部方法,必须将此类定义为抽象类
接口也可以继承另一个接口,使用extends关键字。
interface MyInterface{
String s=“MyInterface”;
public void absM1();
}
interface SubInterface extends MyInterface{
public void absM2();
}
public class SubAdapter implements SubInterface{
public void absM1(){System.out.println(“absM1”);}
public void absM2(){System.out.println(“absM2”);}
}
实现类SubAdapter必须给出接口SubInterface以及父接口MyInterface中所有方法的实现。
通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。
通过接口可以指明多个类需要实现的方法,一般用于定义对象的扩张功能。
接口主要用来定义规范。解除耦合关系。
接口和抽象类之间的关系
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。
接口中的默认方法
接口默认方法的“类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么实现类必须覆盖该方法来解决冲突
类的成员之五:内部类
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
Inner class的名字不能与包含它的类名相同;
Inner class可以使用外部类的私有数据,因为它是外部类的成员,同一个类的成员之间可相互访问。而外部类要访问内部类中的成员需要:内部类.成员或者内部类对象.成员。
分类:
成员内部类(static成员内部类和非static成员内部类)
局部内部类(不谈修饰符)、匿名内部类
内部类举例
class A {
private int s;
public class B{
public void mb() {
s = 100;
System.out.println("在内部类B中s=" + s);
} }
public void ma() {
B i = new B();
i.mb();
} }
public class Test {
public static void main(String args[]){
A o = new A();
o.ma();
} }
内部类举例
public class A{
private int s = 111;
public class B {
private int s = 222;
public void mb(int s) {
System.out.println(s); // 局部变量s
System.out.println(this.s); // 内部类对象的属性s
System.out.println(A.this.s); // 外层类对象属性s
}
}
public static void main(String args[]){
A a = new A();
A.B b = a.new B();
b.mb(333);
}}
内部类特性
Inner class作为类的成员:
可以声明为final的
和外部类不同,Inner class可声明为private或protected;
Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
Inner class作为类:
可以声明为abstract类 ,因此可以被其它的内部类继承
【注意】非static的内部类中的成员不能声明为static的,只有在外部类或static的内部类中才可声明static成员。
匿名内部类
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
new 父类构造器(实参列表)|实现接口(){
//匿名内部类的类体部分
}
如
interface A{
public abstract void fun1();
}
public class Outer{
public static void main(String[] args) {
new Outer().callInner(new A(){
//接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名
public void fun1() {
System.out.println(“implement for fun1");
}
});// 两步写成一步了
}
public void callInner(A a) {
a.fun1();
}
}