Java基础部分2
Java 基础知识
一、 package 和 import
package
其实包就是把很多很多的代码进行整理,便于代码的统一管理。
①怎么定义包:在java源码第一行编写 package 语句。注意:package语句只能出现在java代码第一行。
②包名命名规范中要求是全部小写。
③包名命名规范:公司域名倒序 + 项目名 + 模块名 + 功能名。例如:
①com.powernode.oa.empgt.service
④如果带包编译:
①javac -d 编译后的存放目录 java源文件路径
⑥有了包机制后,完整类名是包含包名的,例如类名是:com.powernode.javase.chapter02.PackageTest
import
①import语句用来引入其他类。
②A类中使用B类,A类和B类不在同一个包下时,就需要在A类中使用import引入B类。
③java.lang包下的不需要手动引入。
④import语句只能出现在package语句之下,class定义之前。
⑤import语句可以编写多个。
⑥import语句可以模糊导入:java.util.*;
⑦import静态导入:import static java.lang.System.*;
二、面向对象概述
1. 面向对象三大特征
面向对象三大特征
①封装(Encapsulation)
②继承(Inheritance)
③多态(Polymorphism)
2.对象的创建与使用
1 | public class Student { |
①对象的创建
1 | Student s = new Student(); |
在Java中,使用class定义的类,属于引用数据类型。所以Student属于引用数据类型。类型名为:Student。
Student s; 表示定义一个变量。数据类型是Student。变量名是s。
②对象的使用
读取属性值:s.name
修改属性值:s.name = “jackson”;
③通过一个类可以实例化多个对象
Student s1 = new Student();
Student s2 = new Student();
④ public static void functionA(),直接用类名.functionA()来访问
这个叫做静态方法
如果描述对象的动作,就不加static ,这种方法叫做实例方法
3.JVM内存结构图
- 元空间(mataspace)存储的是类的元信息,字节码等。元空间是在java8后面引入的。
JVM java虚拟机中定义的规范叫做方法区。
==一旦引用为NULL,表示引用不再指向对象了==
4.封装
什么是封装?
封装是一种将数据和方法加以包装,使之成为一个独立的实体,并且把它与外部对象隔离开来的机制。具体来说,封装是将一个对象的所有“状态(属性)”以及“行为(方法)”统一封装到一个类中,从而隐藏了对象内部的具体实现细节,向外界提供了有限的访问接口,以实现对对象的保护和隔离。
封装的好处
封装通过限制外部对对象内部的直接访问和修改,保证了数据的安全性,并提高了代码的可维护性和可复用性。
在代码上如何实现封装
1. 属性私有化,对外提供getter和setter方法。
属性私有化:使用private来进行修饰。
属性私有化的作用是禁止外部程序随意访问。
2. 为了保证外部程序以然可以访问age属性,还是需要提供一个公开的外部访问的入口。
访问一般分为两个方法,一个负责读,一个负责修改。
读取方法的格式:
1 | public int getAge(){} |
改方法的格式:
1 | public void setAge(int age){} |
实例方法调用实例方法
还是一个this.pay()
5. 构造方法(构造器)constructor
其实就是C里面的构造函数
构造方法的作用
- 实现对象的出啊关键,通过调用的构造方法啊可以完成对于对象的创建
- 实现对于对象的各种属性的赋值。
构造方法怎么定义?
构造方法最后执行结束之后,会返回这个new出来的实例的内存地址,但是构造方法中不需要提供return语句
构造方法名一定要和类名保持一致!
构造方法不需要返回值,如果有返回值就变成了普通方法
缺省构造器
如果一个类中没有显示的去定义构造方法,就会提供一个默认的方法,叫做缺省构造器。
当在调用 new Classname的时候,其实就是在调用构造函数
完成构建,属性赋默认值。
如果一个类里面提供了构造函数,那么就不会再创建缺省的构造方法,所以最好还是写一个无参数的构造方法。
构造方法Constructor(构造器)
①构造方法有什么作用?
1.构造方法的执行分为两个阶段:对象的创建和对象的初始化。这两个阶段不能颠倒,也不可分割。
2.在Java中,当我们使用关键字new时,就会在内存中创建一个新的对象,虽然对象已经被创建出来了,但还没有被初始化。而初始化则是在执行构造方法体时进行的。
②构造方法如何定义?
[修饰符列表] 构造方法名(形参){}
③构造方法如何调用?new 构造方法名(实参);
④关于无参数构造方法:如果一个类没有显示的定义任何构造方法,系统会默认提供一个无参数构造方法,也被称为缺省构造器。一旦显示的定义了构造方法,则缺省构造器将不存在。为了方便对象的创建,建议将缺省构造器显示的定义出来。
⑤构造方法支持重载机制。
⑥关于构造代码块。对象的创建和初始化过程梳理:
①new的时候在堆内存中开辟空间,给所有属性赋默认值
②执行构造代码块进行初始化
③执行构造方法体进行初始化
④构造方法执行结束,对象初始化完毕。
注意,set是修改,构造方法是初始化
6. this关键字
this关键字
①this是一个关键字。
②this出现在实例方法中,代表当前对象。语法是:this.
③this本质上是一个==引用==,该引用保存当前对象的内存地址。
④通过“this.”可以访问实例变量,可以调用实例方法。
⑤this存储在:栈帧的局部变量表的第0个槽位上。
⑥this. 大部分情况下可以省略,用于区分局部变量和实例变量时不能省略。
⑦this不能出现在静态方法中。
public static vois functionstatic(){}
这里面不可以用this,因为static方法中没有当前对象,所以不可以用this。
但是在静态方法中可以调用另外一个静态方法。
⑧“this(实参)”语法:
①只能出现在构造方法的第一行。
②通过当前构造方法去调用本类中其他的构造方法。
③作用是:代码复用。
实例方法只可以引用.
7.static关键字
final变量不能修改,static变量可以在这个类里头修改
①static是一个关键字,翻译为:静态的。
②static修饰的变量叫做静态变量。当所有对象的某个属性的值是相同的,建议将该属性定义为静态变量,来节省内存的开销。
③静态变量在类加载时初始化,存储在堆中。
④static修饰的方法叫做静态方法。
所有static级别的,都是类级别的,直接用类名来访问
⑤所有静态变量和静态方法,统一使用“类名.”调用。==虽然可以使用“引用.”来调用,但实际运行时和对象无关,所以不建议这样写,因为这样写会给其他人造成疑惑。==
⑥使用“引用.”访问静态相关的,即使引用为null,也不会出现空指针异常。
⑦静态方法中不能使用this关键字。因此无法直接访问实例变量和调用实例方法。
⑧静态代码块在类加载时执行,一个类中可以编写多个静态代码块,遵循自上而下的顺序依次执行。
⑨静态代码块代表了类加载时刻,如果你有代码需要在此时刻执行,可以将该代码放到静态代码块中。
静态变量内存图
空指针异常
静态方法中无法直接访问实例相关的数据。
静态代码块
语法结构
语法格式:
1
2
3static{
这个括号叫做静态上下文,
}静态代码块在类加载的时候执行,并且只执行了一次
静态代码块可以编写多个,并且遵循自上而下的顺序执行。
按照以上的这个代码,main方法也是最后执行的
此外
按照这个,会报错。连main方法都没有执行,name也没有执行,
:在静态上下文中无法直接访问实例相关的数据,但是可以访问在这个静态代码块前面定义的静态数据。
在这个静态代码块之后定义的就不能访问了。因为静态的数据是按照顺序进行的。
8.JVMjava虚拟机的体系结构
==JVM对应了一套规范(Java虚拟机规范),它可以有不同的实现==
①JVM规范是一种抽象的概念,它可以有多种不同的实现。例如:
1.HotSpot:HotSpot 由 Oracle 公司开发,是目前最常用的虚拟机实现,也是默认的 Java 虚拟机,默认包含在 Oracle JDK 和 OpenJDK 中
2.JRockit:JRockit 也是由 Oracle 公司开发。它是一款针对生产环境优化的 JVM 实现,能够提供高性能和可伸缩性
3.IBM JDK:IBM JDK 是 IBM 公司开发的 Java 环境,采用了与 HotSpot 不同的 J9 VM,能够提供更小的内存占用和更迅速的启动时间
4.Azul Zing:Azul Zing 是针对生产环境优化的虚拟机实现,能够提供高性能和实时处理能力,适合于高负载的企业应用和实时分析等场景
5.OpenJ9:OpenJ9 是由 IBM 开发的优化的 Java 虚拟机实现,支持高度轻量级、低时延的 GC、优化的 JIT 编译器和用于健康度测试的可观察性仪表板
②右图是从oracle官网上截取的Java虚拟机规范中的一部分。(大家也可以找一下oracle官方文档)
③我们主要研究运行时数据区。运行时数据区包括6部分:
The pc Register(程序计数器):下一个你要执行的字节码指令
Java Virtual Machine Stacks(Java虚拟机栈)
Heap(堆)
Method Area(方法区)
Run-Time Constant Pool(运行时常量池)
Native Method Stacks(本地方法栈)
1 | JVM规范中的运行时数据区 |
**
9、设计模式概述
单例模式(==GoF==23种设计模式之一,最简单的设计模式:==如何保证某种类型的对象只创建一个)==
//饿汉式单例模式:不管这个对象用还是不用,提前把对象给创建好了。
step1:构造函数私有化//不让new,但是又可以获得方法
step2:对外提供一个公开的静态方法,用这个方法获取单个实例
step3: 定义一个静态变量,在类加载的时候,初始化静态变量(只初始化一次)。
//懒汉式单例模式:等到用的时候再创建,不用不创建。
step1:构造方法私有化
step2:对外提供一个静态方法,可以通过这个方法获取到对象;
这个是初步的,等到以后多线程的时候,还需要 改进
10.继承
① 面向对象三大特征之一:继承
② 继承作用?
l基本作用:代码复用
l重要作用:有了继承,才有了方法覆盖和多态机制。
③继承在java中如何实现?
l[修饰符列表] class 类名 extends 父类名{}
lextends翻译为扩展。表示子类继承父类后,子类是对父类的扩展。
④继承相关的术语:当B类继承A类时
lA类称为:父类、超类、基类、superclass
lB类称为:子类、派生类、subclass
⑤Java只支持单继承,一个类只能直接继承一个类。
⑥Java不支持多继承,但支持多重继承(多层继承)。
⑦子类继承父类后,除私有的不支持继承、构造方法不支持继承。其它的全部会继承。
==⑧一个类没有显示继承任何类时,默认继承java.lang.Object类。==比如tostring就是object的,任何一个类都可以调用
11.方法覆盖
方法覆盖/override/方法重写/overwrite,是在编译器层面的功能,方法重载机制是给编译器看的
①什么情况下考虑使用方法覆盖?
1.当从父类中继承过来的方法无法满足当前子类的业务需求时。
②发生方法覆盖的条件?
具有==继承关系的父子类之间==
==相同的返回值类型,相同的方法名,相同的形式参数列表==
访问权限不能变低,可以变高。
高———————>低
- 抛出异常不能变多,可以变少。
父类抛了,子类不抛,比如
- 返回值类型可以是父类方法返回值类型的子类。
③方法覆盖的小细节:
@Override注解标注的方法会在编译阶段检查该方法是否重写了父类的方法。
==私有方法不能继承,所以不能覆盖。==
==构造方法不能继承,所以不能覆盖。==
==静态方法不存在方法覆盖,方法覆盖针对的是实例方法。==
==方法覆盖说的实例方法,和实例变量无关。(==可以写程序测试一下)
在java中也有个注解,在编译的时候可以检查这个方法是否是重写了父类的方法@Override
@Override 只在编译阶段会有用,和运行期无关.
如果返回值类型是引用类型,那么这个返回值类型可以是原类型的子类型
12.多态
多态的基础语法
①什么是向上转型和向下转型?
java允许具有继承关系的父子类型之间的类型转换。
==向上转型(upcasting):子–>父==
==l子类型的对象可以赋值给一个父类型的引用。==
- ==向下转型(downcasting):父–>子==
一般来说,想要调用的方法是子类当中特有的方法的时候,才会想要向下转型.
==l父类型的引用可以转换为子==类型的引用。但是需要加强制类型转换符。
- 无论是向上转型还是向下转型,前提条件是:==两种类型之间必须存在继承关系。这样编译器才能编译通过。==
②什么是多态?
编译过程中是一个形态,运行的时候又是另外一个形态.
父类型引用指向子类对象。Animal a = new Cat(); a.move();
程序分为编译阶段和运行阶段:
l编译阶段:编译器只知道a是Animal类型,因此去Animal类中找move()方法,找到之后,绑定成功,编译通过。这个过程通常被称为静态绑定。
l运行阶段:运行时和JVM堆内存中的真实Java对象有关,所以运行时会自动调用真实对象的move()方法。这个过程通常被称为动态绑定。
- 多态指的是:多种形态,编译阶段一种形态,运行阶段另一种形态,因此叫做多态。
③ instanceof运算符可以解决类型转换问题
- instanceof运算符的结果一定是:true/false
- 语法结构
1 | (a instanceof cat) |
这个运算符主要就是用来进行判断.
1 | if(x instanceof Bird){ |
④ 软件开发七大原则
软件开发原则旨在引导软件行业的从业者在代码设计和开发过程中,遵循一些基本原则,以达到高质量、易维护、易扩展、安全性强等目标。软件开发原则与具体的编程语言无关的,属于软件设计方面的知识。
==开闭原则 (Open-Closed Principle,OCP):一个软件实体应该对扩展开放,对修改关闭。即在不修改原有代码的基础上,通过添加新的代码来扩展功能。(最基本的原则,其它原则都是为这个原则服务的。)==
单一职责原则:一个类只负责单一的职责,也就是一个类只有一个引起它变化的原因。
里氏替换原则:子类对象可以替换其基类对象出现的任何地方,并且保证原有程序的正确性。
接口隔离原则:客户端不应该依赖它不需要的接口。
依赖倒置原则:高层模块不应该依赖底层模块,它们都应该依赖于抽象接口。换言之,面向接口编程。
迪米特法则:一个对象应该对其它对象保持最少的了解。即一个类应该对自己需要耦合或调用的类知道得最少。
合成复用原则:尽量使用对象组合和聚合,而不是继承来达到复用的目的。组合和聚合可以在获取外部对象的方法中被调用,是一种运行时关联,而继承则是一种编译时关联。
⑤ 多态在开发中的作用
降低程序的耦合度,提高程序的扩展力。
尽量使用多态,面向抽象编程,不要面向具体编程。
⑥多态的基础语法
向下转型我们需要注意什么?
向下转型时,使用不当,容易发生类型转换异常:ClassCastException。
在向下转型时,一般建议使用instanceof运算符进行判断来避免ClassCastException的发生。
instanceof运算符的使用
语法格式:(引用 instanceof 类型)
执行结果是true或者false
例如:(a instanceof Cat)
l如果结果是true:表示a引用指向的对象是Cat类型的。
l如果结果是false:表示a引用指向的对象不是Cat类型的。
13 . 抽象类
注意!抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类。
此外,public和abstract没有顺序关系
①什么时候考虑将类定义为抽象类?
如果类中有些方法无法实现或者没有意义,可以将方法定义为抽象方法。类定义为抽象类。这样在抽象类中只提供公共代码,具体的实现强行交给子类去做。比如一个Person类有一个问候的方法greet(),但是不同国家的人问候的方式不同,因此greet()方法具体实现应该交给子类。再比如主人喂养宠物的例子中的宠物Pet,Pet中的eat()方法的方法体就是没有意义的。
②抽象类如何定义?
abstract class 类名{}
③抽象类有构造方法,但无法实例化。抽象类的构造方法是给子类使用的。
④抽象方法如何定义?
abstract 方法返回值类型 方法名(形参);
没有方法体
⑤抽象类中不一定有抽象方法,但如果有抽象方法那么类要求必须是抽象类。
==⑥一个非抽象的类继承extend抽象类,要求必须将抽象方法进行实现/重写。==
需要把全部的抽象方法全部给实现了
⑦abstract关键字不能和private,final,static关键字共存。
private: private是不能被继承覆盖的,而abstract修饰的是一定要被继承实现的,存在冲突.
final: 不能继承
static:不能被覆盖
⑧抽象类需要构造方法,但是无法实现实例化,这个构造方法是给其子类使用的.
14. super关键字
①super关键字和this关键字对比来学习。this代表的是当前对象。super代表的是当前对象中的父类型特征。
②super不能使用在静态上下文中。就是在编写
③“super.”大部分情况下是可以省略的。什么时候不能省略?
==当父类和子类中定义了相同的属性(实例变量)或者相同方法(实例方法)时,如果需要在子类中访问父类的属性或方法时,super.不能省略。==
④this可以单独输出,super不能单独输出。
this本身是一个引用,所以可以直接输出,super不是引用,不能直接输出
1 | system.out.println(this);//√ |
⑤super(实参); 通过子类的构造方法调用父类的构造方法,目的是为了完成父类型特征的初始化。
⑥当一个构造方法第一行没有显示的调用“super(实参);”,也没有显示的调用“this(实参)”,系统会自动调用super()。因此一个类中的无参数构造方法建议显示的定义出来。
⑦super(实参); 这个语法只能出现在构造方法第一行。
this()和super()
⑧在Java语言中只要new对象,Object的无参数构造方法一定会执行。
子类的构造方法中,就算没有写super();,也是会跑这个代码.
1 | super(); |
15.final关键字
这个关键字表示是最终的,被final所修饰的类不能被 继承
①final修饰的类不能被继承
②final修饰的方法不能被覆盖
③final修饰的变量,一旦赋值不能重新赋值
④final修饰的实例变量必须==在对象初始化时==手动赋值
必须在构造方法执行完之前,手动附上值.不允许采用系统的默认值
1 | final class Mystring{ |
⑤final修饰的实例变量一般和static联合使用:==称为常量==
因为类中定义的final 修饰的变量不能修改,最直接的理解就是所有的实例变量的这个变量都是一个值,因此,假设new出来了100个,那么这100个类的这个字段也都是同一个,所以可以直接加上static,设置成一个静态的,更方便,也更省堆空间.
1 | static final double MATH_PAI = 3.1415926 |
⑥final修饰的引用,一旦指向某个对象后,不能再指向其它对象。但指向的对象内部的数据是可以修改的。
1 | final 修饰对象,内存地址不能改,但是内存地址里面的东西还是可以修改的 |
16.接口
接口的基础语法
==①接口(interface)在Java中表示一种规范或契约,它定义了一组抽象方法和常量,用来描述一些实现这个接口的类应该具有哪些行为和属性==。接口和类一样,也是一种引用数据类型。
②接口怎么定义?[修饰符列表] interface 接口名{}
1 | public interface Myinterface{ |
③抽象类是半抽象的(抽象类可以定义抽象方法也可以定义非抽象方法 ,有构造方法不能new对象),
接口是完全抽象的。==接口没有构造方法,也无法实例化。==
④接口中只能定义:常量+抽象方法。==接口中的常量的static final可以省略==。接口中的抽象方法的abstract可以省略。接口中所有的方法和变量都是public修饰的。(JDK8)
⑤接口和接口之间可以多继承。
⑥类和接口的关系我们叫做实现(**==这里的实现也可以等同看做继承==)。使用==implements==**关键字进行接口的实现。
⑦一个非抽象的类实现接口必须将接口中所有的抽象方法全部实现。
⑧一个类可以实现多个接口。语法是:class 类 implements 接口A,接口B{}
⑨Java8之后,接口中允许出现默认方法和静态方法(JDK8新特性)
引入默认方式是为了解决接口演变问题:接口可以定义抽象方法,但是不能实现这些方法。所有实现接口的类都必须实现这些抽象方法。这会导致接口升级的问题:当我们向接口添加或删除一个抽象方法时,这会破坏该接口的所有实现,并且所有该接口的用户都必须修改其代码才能适应更改。这就是所谓的”接口演变”问题。
如果还是按照之前的,那么如果需要在接口中加入一个新的方法,按照接口的实现类需要把借口中的全部都给实现了这一个要求,这个接口的所有实现类都需要实现这个接口,这太麻烦了.
加入默认方法后,实现该接口的实现类中都有这个默认方法,但是不需要去实现
引入的静态方法只能使用本接口名来访问,无法使用实现类的类名访问。
原因:接口也可以当工具使用,渐渐变成了工具类
默认方法:
静态方法:
1 | static void staticmethod(){ |
⑩JDK9之后允许接口中定义私有private的实例方法(为默认方法服务的)和私有的静态方法(为静态方法服务的)。
⑪所有的接口隐式的继承Object。因此接口也可以调用Object类的相关方法。
接口的作用
①面向接口调用的称为:接口调用者
②面向接口实现的称为:接口实现者
③调用者和实现者通过接口达到了解耦合。也就是说调用者不需要关心具体的实现者,实现者也不需要关心具体的调用者,双方都遵循规范,面向接口进行开发。
④面向抽象编程,面向接口编程,可以降低程序的耦合度,提高程序的==扩展力==。
⑤例如定义一个Usb接口,提供read()和write()方法,通过read()方法读,通过write()方法写:
①定义一个电脑类Computer,它是调用者,面向Usb接口来调用。
②Usb接口的实现可以有很多,例如:打印机(Printer),硬盘(HardDrive)。
1 | public class Computer{ |
⑦再想想,我们平时去饭店吃饭,这个场景中有没有接口呢?食谱菜单就是接口。顾客是调用者。厨师是实现者。
接口与抽象类如何选择
①抽象类和接口虽然在代码角度都能达到同样的效果,但适用场景不同:
抽象类主要适用于公共代码的提取。当多个类中有共同的属性和方法时,为了达到代码的复用,建议为这几个类提取出来一个父类,在该父类中编写公共的代码。如果有一些方法无法在该类中实现,可以延迟到子类中实现。这样的类就应该使用抽象类。
接口主要用于功能的扩展。例如有很多类,一些类需要这个方法,另外一些类不需要这个方法时,可以将该方法定义到接口中。需要这个方法的类就去实现这个接口,不需要这个方法的就可以不实现这个接口。接口主要规定的是行为。
注意:一个类继承某个类的同时可以实现多个接口:class 类 extends 父类 implements 接口A,接口B{}
注意:当某种类型向下转型为某个接口类型时,接口类型和该类之间可以没有继承关系,编译器不会报错的。
17. UML(统一建模语言概述)
考虑一下类之间的关系.
①UML(Unified Modeling Language,统一建模语言)是一种用于面向对象软件开发的图形化的建模语言。它由Grady Booch、James Rumbaugh和Ivar Jacobson等三位著名的软件工程师所开发,并于1997年正式发布。UML提供了一套通用的图形化符号和规范,帮助开发人员以图形化的形式表达软件设计和编写的所有关键方面,从而更好地展示软件系统的设计和实现过程。
②UML是一种图形化的语言,类似于现实生活中建筑工程师画的建筑图纸,图纸上有特定的符号代表特殊的含义。
③UML不是专门为java语言准备的。只要是面向对象的编程语言,开发前的设计,都需要画UML图进行系统设计。(设计模式、软件开发七大原则等同样也不是只为java语言准备的。)
④UML图包括:
- 类图(Class Diagram):描述软件系统中的类、接口、关系和其属性等;
- 用例图(Use Case Diagram):描述系统的功能需求和用户与系统之间的关系;
- 序列图(Sequence Diagram):描述对象之间的交互、消息传递和时序约束等;
- 状态图(Statechart Diagram):描述类或对象的生命周期以及状态之间的转换;
- 对象图(Object Diagram):表示特定时间的系统状态,并显示其包含的对象及其属性;
- 协作图(Collaboration Diagram):描述对象之间的协作,表示对象之间相互合作来完成任务的关系;
- 活动图(Activity Diagram):描述系统的动态行为和流程,包括控制流和对象流;
- 部署图(Deployment Diagram):描述软件或系统在不同物理设备上部署的情况,包括计算机、网络、中间件、应用程序等。
⑤常见的UML建模工具有:StarUML,Rational Rose等。
类之间的关系
- 泛化关系(is a)//继承关系
1 | 泛化关系(继承关系)is a关系。Cat is a Animal |
- 实现关系(is like a)//定义一个接口,然后有一个实现接口类
1 | 实现关系 is like a 关系。 |
- 关联关系(has a)
1 | 关联关系 has a |
- 聚合关系
聚合关系指的是一个类包含、合成或者拥有另一个类的实例,而这个实例是可以独立存在的。聚合关系是一种弱关联关系,表示整体与部分之间的关系。例如一个教室有多个学生
==生命不绑定在一起==
- 组合关系(Composition)
组合关系是聚合关系的一种特殊情况,表示整体与部分之间的关系更加强烈。组合关系指的是一个类包含、合成或者拥有另一个类的实例,而这个实例只能同时存在于一个整体对象中。如果整体对象被销毁,那么部分对象也会被销毁。==例如一个人对应四个肢体==。
1 | public class Limbs{ |
- 依赖关系(Dependency)
依赖关系是一种临时性的关系,**==当一个类使用另一个类的功能时==**,就会产生依赖关系。如果一个类的改变会影响到另一个类的功能,那么这两个类之间就存在依赖关系。依赖关系是一种较弱的关系,可以存在多个依赖于同一个类的对象。==例如A类中使用了B类,但是B类作为A类的方法参数或者局部变量等。==
1 | public class User{ |
==18. 访问控制权限==
①private:私有的,只能在本类中访问。
②缺省:默认的,同一个包下可以访问。
③protected:受保护的,子类中可以访问。(==受保护的通常就是给子孙用的。==)clone就是protected的
④public:公共的,在任何位置都可以访问。
==①类中的属性和方法访问权限共有四种:private、缺省、protected和public。==
==②类的访问权限只有两种:public和 缺省。==
1 | public class A{ |
③访问权限控制符不能修饰局部变量。
注意:class里面的属性,方法的访问权限是四个;但是对于定义类,是只有两种访问权限的.!
19.Object类
①java.lang.Object是所有类的超类。java中所有类都实现了这个类中的方法。
②Object类是我们学习JDK类库的第一个类。通过这个类的学习要求掌握会查阅API帮助文档。
③现阶段Object类中需要掌握的方法:
toString:将java对象转换成字符串。
equals:判断两个对象是否相等。//返回一个布尔类型
1 | == 的作用: |
④现阶段Object类中需要了解的方法:
hashCode:返回一个对象的哈希值,通常作为在哈希表中查找该对象的键值。Object类的默认实现是根据对象的内存地址生成一个哈希码(即将对象的内存地址转换为整数作为哈希值)。hashCode()方法是为了HashMap、Hashtable、HashSet等集合类进行优化而设置的,以便更快地查找和存储对象。
finalize:当java对象被回收时,由GC自动调用被回收对象的finalize方法,通常在该方法中完成销毁前的准备。
==clone:对象的拷贝。(浅拷贝,深拷贝)==
Java深入理解深拷贝和浅拷贝区别_java深拷贝浅拷贝-CSDN博客
浅拷贝:
Object的默认方法,专门给子类使用的,受保护的,c++实现的
user和usertest不符合三个要求,不能进行clone,那么怎么实现呢?
重写!
1.==建议把重写的修饰符改为public==
2.要实现接口 ==implements Cloneable==(这是一个标志接口,给java虚拟机看的)
protected修饰的只能在同一个包下或者子类中访问。
只有实现了Cloneable接口的对象才能被克隆。
深拷贝:
1 | //这是浅拷贝 |
20.clone
直接赋值
直接赋值的方式没有生产新的对象,只是生新增了一个对象引用**,直接赋值在 Java 内存中的模型大概是这样的
浅拷贝
如果原型对象的成员变量是值类型,将复制一份给克隆对象,也就是说在堆中拥有独立的空间;
如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
换句话说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
也就是新开一个空间,放克隆的对象,但是这个地址里放的还是原来的引用地址
1 | public class Person implements Cloneable { |
1 | public class PersonApp { |
String、Integer 等包装类都是不可变的对象,当需要修改不可变对象的值时,需要在内存中生成一个新的对象来存放新的值,然后将原来的引用指向新的地址,所以在这里我们修改了 person1 对象的 name 属性值,person1 对象的 name 字段指向了内存中新的 name 对象,
但是我们并没有改变 person 对象的 name 字段的指向,所以 person 对象的 name 还是指向内存中原来的 name 地址,也就没有变化
深拷贝
深拷贝也是对象克隆的一种方式,相对于浅拷贝,深拷贝是一种完全拷贝,无论是值类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象,简单点说就是拷贝对象和被拷贝对象没有任何关系,互不影响。深拷贝的通用模型如下
1 | public class PersonDesc implements Cloneable{ |
21.内部类
静态内部类:
1 | 对于静态内部类来说:访问控制权限修饰符在这里都可以使用。 |
由于在创建静态内部类的时候,不用创建外部的那个类,所以不能访问外部类所定义的实例变量和实例方法,但是可以访问静态变量和静态方法,因为静态的(static)是刚开始运行的时候就会跑的,写入编译文件中的
实例内部类
1 | * 实例内部类:等同可以看做实例变量。 |
1 | public class OuterClass { |
局部内部类
①局部内部类:和局部变量一个级别
局部内部类方外类外部的局部变量时,局部变量需要被final修饰。
从JDK8开始,不需要手动添加final了,但JVM会自动添加。
1 | public class OuterClass { |
==匿名内部类==
特殊的局部内部类,没有名字,只能用一次。
1 | package com.powernode.javase.oop45; |