Java面向对象
1.面向过程和面向对象
面向过程
面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。
面向对象
面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。
优缺点比较
面向过程
优点:
流程化使得编程任务明确。效率高,面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序。
缺点:
代码重用性低,扩展能力差,后期维护难度比较大。
面向对象
优点:
结构清晰,程序是模块化和结构化,更加符合人类的思维方式;易扩展,代码重用率高;易维护,系统低耦合的特点有利于减少程序的后期维护工作量。
缺点:
开销大,当要修改对象内部时,对象的属性不允许外部直接存取,所以要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担,增加运行开销,并且使程序显得臃肿。
性能低,由于面向更高的逻辑抽象层,使得面向对象在实现的时候,不得不做出性能上面的牺牲,计算时间和空间存储大小都开销很大。
2.类和对象
类:对一类事物的描述,是抽象的,概念上的定义
对象:是实际存在的该类事物的每个个体,因而也称为实例。
下图中男孩(boy)、女孩(girl)为类(class),而具体的每个人为该类的对象(object):
3.对象的创建和使用
对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字 new 来创建一个对象。
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象。
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}
如何访问实例变量和调用成员方法:
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public void setAge( int age ){
puppyAge = age;
}
public int getAge( ){
System.out.println("小狗的年龄为 : " + puppyAge );
return puppyAge;
}
public static void main(String[] args){
/* 创建对象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("变量值 : " + myPuppy.puppyAge );
}
}
4.属性与局部变量
-
相同点
1.1 定义变量的格式:数据类型 变量名 = 变量值
1.2 先声明,后使用
1.3 变量都有其对应的作用域 -
不同点
2.1 在类中生命的位置不同
属性:直接定义在类的一对{}中
局部变量:声明在方法内、方法i形参、代码块内、构造器形参、构造器内部的变量2.2 关于权限修饰符的不同
属性:可在声明属性时,指明权限,用权限修饰符
局部变量:不可以使用权限修饰符2.3 默认初始化值的情况
属性:同一维数组的默认初始化值相同
局部变量:没有默认初始化值(在调用局部变量前,一定要显示赋值)2.4 在内存中加载的位置
属性:加载到堆空间中
局部变量:加载到栈空间中
5.方法
方法的重载
重载:再某一个类中,允许存在一个以上的同名方法,只要他们的参数个数或参数类型不同即可(同一类,方法名相同,参数列表不同)
两同三不同。
——同一个类,同一个方法名。
——不同:参数列表不同。(类型,个数,顺序不同)
只有返回值不同不构成方法重载。
只有形参的名称不同,不构成方法重载。
与普通方法一样,构造函数也可以重载。
可变个数形参的方法
格式,对于方法的形参:数据类型。。。形参名。
可变个数的形参的方法与同名的方法之间构成重载。
public void sayHello() {
System.out.println("Hello world");
}
public void sayHello(String str1) {
System.out.println("Hello"+str1);
}
public void sayHello(String ...args) {
for(int i=0;i<args.length;i++) {
System.out.println(args[i]);
}
}
可变个数的形参在调用时,个数从零开始,到无穷多个都可以
可变个数形参的方法与本类当中方法名相同,形参类型相同的数组不构成重载
public void sayHello(String ...args) {
for(int i=0;i<args.length;i++) {
System.out.println(args[i]);
}
}
public void sayHello(String[] args) {
for(int i=0;i<args.length;i++) {
System.out.println(args[i]);
}
}
可变个数形参在的方法的形参中,必须声明在末尾。
可变个数形参在的方法的形参中,最多只能有一个可变个数的形参。
方法的参数传递机制
形参:方法声明时,方法小括号内的参数
实参:调用方法时,实际传入的参数的值
值传递机制
- 如果变量是基本数据类型,此时赋值到是变量所保存的数据值。
- 如果变量是引用数据类型,此时赋值的是变量所保存到数据的地址值。
6.OOP特征之一:封装
高内聚:类的内部数据操作细节自己完成,不允许为外部干涉
低耦合:及对外暴露少量的方法用于使用
目的:使其他类只能通过操控类中的对象来直接达到目的,不能看到具体的实现和属性,从而提高了程序的安全性和便利性。隐藏信息,实现便利。
封装性的体现:将属性私有化, 同时,提供公共的方法来获取和设置此属性的值。
public class Animal {
String name;
int age;
private int leg;
public void show(){
System.out.println("名字:"+name+",年龄:"+age+", 腿有"+leg+"条");
}
//对于现实情况,需要对leg进行限制设置,并对leg进行私有化操作(属性的设置)
public void setlegs(int i){
if(i==0||i %2==0){
leg = i;
}else{
System.out.println("----");
}
}
//由于Leg是私有化的,测试类中无法调用,需提供方法(属性的获取)
public int getlegs(){
System.out.println("腿有"+leg+"条");
return leg;
}
}
public class AnimalText {
public static void main(String[] args) {
Animal dog = new Animal();
dog.name ="二哈";
dog.age = 1;
dog.setlegs(4);
dog.show();
dog.getlegs();
}
}
权限修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
- default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
protected 需要从以下两个点来分析说明:
- 子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
- 子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
7.构造器
什么是构造器
构造器,也称构造方法、构造函数。作用是构造出来一个类的实例,确保对象得到初始化。
构造器的格式: 权限修饰符 类名(无参/有参){}。
根据有无参数,可分为无参构造 和有参构造。
如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器,一旦定义了类的构造器之后,系统就不再提供默认的构造方法;
构造器的特性
1.与一般方法名不同的是,构造方法名必须和类名保持一致,并且没有返回值,甚至连void都没有。
2.Java编译器会自动创建无参构造函数,因此在类中,无参构造即使没有,我们也可省略不写。实例化对象时无需赋值
3.倘若类中已存在有参构造函数,则编译器不再提供默认无参构造。实例化对象时需赋值,不然报错。
4.当类实例化一个对象时会自动调用构造方法。
5.不能被static、final、synchronized、abstract和native修饰。构造方法不能被子类继承
6.每个类可以有零个或多个构造方法.
JavaBean:是一种Java语言写成的可重用组件
- 类是公用的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
属性赋值的先后顺序
- 默认初始化值
- 直接初始值
- 构造器中赋值
- 通过“对象.方法” 或 “对象.属性” 的方式赋值
- 在代码块中赋值
4>3>2 / 5>1
8.this关键字
this 可以用来修饰、调用、属性、方法、构造器
this修饰属性和方法
this理解为:当前对象 或 当前正在创建的对象
this.属性
大部分时候,普通方法访问其他方法、成员变量时无须使用 this 前缀,但如果方法里有个局部变量和成员变量同名,但程序又需要在该方法里访问这个被覆盖的成员变量,则必须使用 this 前缀。
public class Teacher {
private String name; // 教师名称
private double salary; // 工资
private int age; // 年龄
// 创建构造方法,为上面的3个属性赋初始值
public Teacher(String name,double salary,int age) {
this.name = name; // 设置教师名称
this.salary = salary; // 设置教师工资
this.age = age; // 设置教师年龄
}
}
在 Teacher 类的构造方法中使用了 this 关键字对属性 name、salary 和 age 赋值,this 表示当前对象。this.name=name
语句表示一个赋值语句,等号左边的 this.name 是指当前对象具有的变量 name,等号右边的 name 表示参数传递过来的数值。
this.方法
this 关键字最大的作用就是让类中一个方法,访问该类里的另一个方法或实例变量。
/**
* 第一种定义Dog类方法
**/
public class Dog {
// 定义一个jump()方法
public void jump() {
System.out.println("正在执行jump方法");
}
// 定义一个run()方法,run()方法需要借助jump()方法
public void run() {
Dog d = new Dog();
d.jump();
System.out.println("正在执行 run 方法");
}
}
在上面的程序中,一共产生了两个 Dog 对象,在 Dog 类的 run( ) 方法中,程序创建了一个 Dog 对象,并使用名为 d 的引用变量来指向该 Dog 对象。在 DogTest 的 main() 方法中,程序再次创建了一个 Dog 对象,并使用名为 dog 的引用变量来指向该 Dog 对象。
1)在 run( ) 方法中调用 jump( ) 方法时是否一定需要一个 Dog 对象?
答案是肯定的,因为没有使用 static 修饰的成员变量和方法都必须使用对象来调用。
2)是否一定需要重新创建一个 Dog 对象?
不一定,因为当程序调用 run( ) 方法时,一定会提供一个 Dog 对象,这样就可以直接使用这个已经存在的 Dog 对象,而无须重新创建新的 Dog 对象了。因此需要在 run() 方法中获得调用该方法的对象,通过 this 关键字就可以满足这个要求。
/**
* 第二种定义Dog类方法
**/
// 定义一个run()方法,run()方法需要借助jump()方法
public void run() {
// 使用this引用调用run()方法的对象
this.jump();
System.out.println("正在执行run方法");
}
this( )访问构造方法
我们在类的构造器中,可以显示的使用“this(形参列表)”方式,调用本类中指定的其它构造器。
构造器中不能通过“this(形参列表)”方式调用自己。
规定:”this(形参列表)“必须声明在当前构造器的首行。
构造器内部,最多只能声明一个“this(形参列表)”,用来调用其它的构造器。
9.关键字package、import
package
1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
import
为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 “import” 语句可完成此功能。
注意:
类文件中可以包含任意数量的 import 声明。import 声明必须在包声明之后,类声明之前。
1. 使用 import XXX.*的方式,表示可以导入XXX包下的所有结构。
2. JDK自带了很多包,包里由很多类,我们可直接使用,java是开源的。
3. 如果使用的类和接口时java.long包下定义的,或在本包下定义的,则可以省略import结构。
4. 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示。
5. 使用”XXX.*“方式表明可以调用XXX包下的所有结构,但是如果使用的是XXX子包下的结构,则任需要显示导入。
6. impact static:导入指定类或接口的静态结构:属性或方法
10.OOP特征二:继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
类的继承格式
class 父类 {
//基类、超类、superclass
}
class 子类 extends 父类 {
//派生类、subclass
}
为什么需要继承
减少了代码的冗杂,提高了代码的重用性。便于功能的扩展。为之后多态性的使用提供了前提。
特性
一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有属性和方法,特别的,父类中声明为private 的属性和方法,子类继承父类以后,任然以为获取了父类中私有的结构,只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类(直接父类),多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
java.long.Object 类是所有java类(除java.long.Object 类之外)的父类,任何类都默认继承java.long.Object.是JDk 提供的。
11.方法的重写
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
为什么要重写?
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
方法的重写规则
方法名和参数列表与被重写方法的方法名和参数列表必须完全相同
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
构造方法不能被重写。
声明为 final 的方法不能被重写。
声明为 static 的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void.
父类被重写的方法的返回值类型是A类型,,则子类重写的方法的返回值类型可以是A类或A类的子类。
父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型只能是基本数据类型。
子类中重写的方法抛出的异常类型不大于父类中被重写的方法抛出的异常类型
12.super
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
super理解为:父类的,可用来调用属性、方法、构造器
我们在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显示的调用父类中声明的属性或方法,但通常情况下省略。
特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显示的使用“super.属性”的方式,表明调用的是父类中声明的属性。
特殊情况:当子类重写了父类中的方法以后,我们想在子类中调用父类中被重写的方法时,则必须显示的使用“super.方法”的方式,表明调用的是父类中被重写的方法。
我们可以在子类的构造器中显示的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器。
“super.(形参列表)”的使用,必须声明在于子类构造器的首行。
我们在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现。
在构造器的首行,没有显式的声明“this(形参列表)”或“super(形参列表)”,则默认调用的是父类中空参的构造器。
在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器。
13.OOP特征三:多态性
多态是同一个行为具有多个不同表现形式或形态的能力。
对象的多态性:父类的引用指向子类的对象,
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:Parent p = new Child();
多态性的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写的方法。(编译,看左边;运行,看右边)
对象的多态性,只适用于方法,不适用于属性。(编译和运行都看左边)
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
向下转型
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。
如何才能调用子类特有的属性和方法呢?
- 向下转型,使用强制类型转换符,但可能出现ClassCastException的异常。
instanceof
- a instanceof A:判断对象a是否是类A的实列。如果是,返回true,如果不是,返回false.
- 为了避免出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型。
14.Object类
Object 是Java类库中的一个特殊类,也是所有类的父类。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 Object 类
equals()
==和equals() 的区别?
== 运算符
可以使用在基本数据类型变量和引用数据类型变量中
如果此时的是基本数据类型变量,比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量,比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体。
equals()方法
equals()是方法,而非运算符。只能适用于引用数据类型。
Object类中equals()的定义
public boolean equals(Object obj){
return (this == obj);
}
说明,Object类中定义的equals()和== 的使用是相同的,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体。
String 、Date、File、包装类等都重写了Object 类中的equals () 方法,重写以后,比较的不是两个引用的地址值是否相同,而是比较两个对象的“实体内容”是否相同。
equals()的重写
public class Order {
private int orderid;
private String orderName;
@Override
public boolean equals(Object obj){
if(this == obj){
return true;
}if(obj instanceof Order){
Order order = (Order)obj;
return this.orderid == order.orderid && this.orderName.equals(order.orderName);
}
return false;
}
}
toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 当我们能出一个对象的引用时,实际上就是调用当前对象的toString()
- Object类中toStrong()输出对象的地址。
- 在String、Date、File、包装类等都重写了Object类中的toStrong()方法。
public class Test {
public static void main(String[] args) {
Demo demo = new Demo();
System.out.println(demo.toString());//myjava.Demo@1b6d3586
System.out.println(demo);//myjava.Demo@1b6d3586
String str = new String("2022");
System.out.println(str);//2022
Date date = new Date(1234653112L);
System.out.println(date);//Thu Jan 15 14:57:33 CST 1970
}
}
class Demo{}
15.包装类
byte | short | int | long | float | double | char | boolean | |
---|---|---|---|---|---|---|---|---|
位数 | 8 | 16 | 32 | 64 | 32 | 64 | 16 | 1 |
字节数 | 1 | 2 | 4 | 8 | 4 | 8 | 2 | 1(1/8) |
默认值 | 0 | 0 | 0 | 0L | 0.0f | 0.0d | false | |
包装类型 | Byte | Short | Integer | Long | Float | Double | Character | Boolean |
基本数据类型——>包装类
public class WrapperTest {
public static void main(String[] args) {
// 基本数据类型——>包装类
int num = 10;
Integer integer = new Integer(num);
Integer integer1 = new Integer("123");
System.out.println(integer);//10
System.out.println(integer1);//123
Float f1 = new Float(12.7f);
Float f2 = new Float("12.7");
System.out.println(f1);//12.7
System.out.println(f2);//12.7
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("aaaaa");
System.out.println(b1);//true
System.out.println(b2);//false
}
}
包装类——>基本数据类型
public class WrapperTest {
public static void main(String[] args) {
// 包装类 ——>基本数据类型 调用包装类的 .xxxValue()
Integer in1 = new Integer(12);
int intValue = in1.intValue();
System.out.println(intValue+1);//13
Float f1 = new Float(12.8);
float floatValue = f1.floatValue();
System.out.println(floatValue+1);//13.8
}
}
JDK5.0新特性:自动装箱/自动拆箱
public class WrapperTest {
public static void main(String[] args) {
// 基本数据类型——>包装类 自动装箱
int num = 10;
Integer integer = num;
boolean bool= true;
Boolean bool2 = bool;
System.out.println(integer);
System.out.println(bool2);
// 包装类 ——>基本数九类型 自动拆箱
int num2 = integer;
System.out.println(num2+1);
}
}
基本数据类型/包装类 ——>String
public class WrapperTest {
public static void main(String[] args) {
// 基本数据类型/包装类 ——>String String.valueOf();
int num =10;
Integer integer = new Integer(num);
String s1 = String.valueOf(num);
String s2 = String.valueOf(integer);
System.out.println(s1);
System.out.println(s2);
}
}
String ——>基本数据类型/包装类
public class WrapperTest {
public static void main(String[] args) {
// String ——>基本数据类型/包装类 调用包装类的parseInt();
String str = "123";
int in1 = Integer.parseInt(str);
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(in1);
System.out.println(b1);
}
}
16.static
stutic: 静态的,可以用来修饰属性、方法、代码块、内部类。
使用static 修饰属性、静态变量
- 实例变量(非静态变量):我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态变量,当修改其中一个对象的非静态变量时,不会导致其它对象中同样的属性值的改变。
- 静态变量:我们创建了类的多个对象,多个对象共同享用一个静态变量,当通过某一个对象修改静态变量时,会导致其它对象调用此静态变量时,是修改过的。
静态变量随着类的加载而加载,可以通过“类.静态变量”的方法进行调用。说明静态变量的加载要早于对象的加载。
由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。
使用static 修饰方法,静态方法
-
随着类的加载而加载,可以通过“类.静态方法”的方式进行调用。
-
静态方法中,只能调用静态的方法和属性,非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
在静态方法中,不能使用this 关键字,super关键字。
单例设计模式
设计模式:在大量的实践中总结和理论化之后优选的代码结构,编程风格、以及解决问题的思考方式。
类的单例设计模式:就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
class Bank {
//饿汉式
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象,要求此对象也必须声明为静态的。
private static Bank instance = new Bank();
//3. 提供公共的静态方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
public class Order {
//懒汉式
//1. 私有化类的构造器
private Order(){
}
//2.声明当前类的对象,没有初始化。
private static Order instance = null;
//3. 声明public、static 的返回当前类对象的方法。
public static Order getInstance(){
if(instance == null){
instance = new Order();
}
return instance;
}
}
饿汉式 vs 懒汉式
饿汉式: 好处:对象加载时间过长。坏处:线程安全的。
懒汉式: 好处:延迟对象的创建, 坏处:目前的写法线程不安全
17.代码块
在Java中,使用{}括起来的代码被称为代码块(Code block)
代码块的作用 用来初始化类、对象。
静态代码块:
内部可以有输出语句,随着类的加载而执行,而且只能直行一次。
作用:初始化类的信息。
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
非静态代码块:
内部可以有输出语句,随着对象的创建而执行,每创建一个对象,就执行一次非静态代码块。
作用:可以在创建对象时,对对象的属性等进行初始化,
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
18.final
final 关键字是什么?
final 在 Java 中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作 final,你将不能改变这个引用了,编译器会检查代码,如果试图将变量再次初始化的话,编译器会报编译错误。
final 用来修饰一个类:此类不能被其它类所继承。
final class SuperClass {
}
class SubClass extends SuperClass { //编译错误
}
final 用来修饰方法,表明此方法不可以被重写。
final 用来修饰变量,此时的“变量”就是一个常量
- final 修饰属性:可以考虑赋值的位置有:显示初始化、代码快中初始化、构造器中初始化。
- final 修饰局部变量: 尤其是使用final修饰形参时,表明此形参是一个常量,当我们调用此方法时,给常量形参一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能重新赋值。
19.抽象类与抽想方法
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
abstract 可以用来修饰的结构:类、方法。
抽象类
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
抽象类中一定有构造器,便于子类对象实例化时调用。开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。
抽象方法
抽象方法只有方法的声明,无方法体。方法名后面直接跟一个分号,而不是花括号。
public abstract double computePay();
声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
abstract不能用来修饰:属性、构造器等结构
abstract 不能用来修饰私有方法、静态方法、final的方法、final的类。
20.接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。
定义接口中的成员
- JDK7及以前,只能定义全局常量和抽象方法
- 全局变量:public static final的
- 抽象方法:public abstract的
- JDK8,除了定义全局变量和抽象方法之外,还可以定义静态方法、默认方法
接口的声明语法格式如下:
interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
接口中不能定义构造器!意味着接口不可以实例化
Java开发中,接口通常让类去实现(implements)的方式来使用。
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
在类声明中,Implements关键字放在class声明后面。
class AA extends BB implements CC,DD,EE
Java类可以实现多个接口,弥补了Java单继承性的局限性。
接口与接口之间可以继承,而且是多继承。
接口的具体使用,体现多态性
接口,实际上可以看做是一种规范。
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
computer.transforDate(flash);
//2.创建了接口的非匿名实现类的匿名对象
computer.transforDate(new Printer());
//3.创建了接口的匿名实现类的非匿名对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void end() {
System.out.println("手机结束工作");
}
};
computer.transforDate(phone);
//4.创建了接口的匿名实现类的匿名对象
computer.transforDate(new USB() {
@Override
public void start() {
System.out.println("机器开始工作");
}
@Override
public void end() {
System.out.println("机器结束工作");
}
});
}
}
interface USB{
void start();
void end();
}
class Computer{
public void transforDate(USB usb){//USB usb = new Flash();
usb.start();
System.out.println("输出具体的数据");
usb.end();
}
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开始工作");
}
@Override
public void end() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void end() {
System.out.println("打印机结束工作");
}
}
代理模式
public class NetWorkText {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
public void check(){
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
JDK8,除了定义全局变量和抽象方法之外,还可以定义静态方法、默认方法
public class SuperClass {
public void method3(){
System.out.println("SuperClass:广州");
}
}
public interface CompareA {
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海");
}
default void method3(){
System.out.println("CompareA:上海");
}
}
public interface CompareB {
default void method3(){
System.out.println("CompareB:广州");
}
}
public class SubClassText {
public static void main(String[] args) {
SubClass subClass = new SubClass();
// 接口中定义的静态方法,只能通过接口来调用
// subClass.method1();
CompareA.method1();
//通过实现类的对象,可以调用接口中的默认方法。
subClass.method2();
//如果子类(或实现类)继承的父类和实现的接口声明了同名同参数的方法。
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。
// 接口冲突:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
//那么在实现类没有重写此方法的情况下,报错!!!
//这就需要我们必须在实现类中重写此方法。
subClass.method3();
subClass.myMethod();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
@Override
public void method2(){
System.out.println("SubClass:上海");
}
@Override
public void method3(){
System.out.println("SubClass:天津");
}
//在子类(或 实现类)的方法中调用父类、接口中的方法。
public void myMethod(){
method3();//调用重写的方法
super.method3();//调用的是父类的方法
//调用接口中的默认方法
CompareA.super.method3();
}
}
21.内部类
Java 一个类中可以嵌套另外一个类,语法格式如下:
class OuterClass { // 外部类
// ...
class NestedClass { // 嵌套类,或称为内部类
// ...
}
}
- 成员内部类(静态、非静态)
- 局部内部类(代码块内、构造器内、方法内)
成员内部类
作为外部类的成员:
- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰符修饰
作为一个类:
- 可以定义属性、方法、构造器
- 可以被final 修饰,表示此类不能被继承,而不适用final,可以被继承。
口中的默认方法。 - 可以被abstract修饰