Java从入门到精通
Java从入门到精通
- 一、Java基础部分
- (一)核心基础
- 1、常用算法
- (1)二分法查找
- (2)冒泡排序
- (3)快速排序
- (4) 算法复杂度
- (5)Arrays工具类的使用
- 2、面向对象上
- (1)Java面向对象学习的三条主线:
- (2)面向过程(POP) 与 面向对象(OOP)
- (3)面向对象的三大特征
- (4)面向对象的思想概述
- (5)Java类及类的成员
- (6)对象的创建和使用:内存解析
- (7)类的成员之一:属性
- (8)类的成员之二:方 法(method)
- (9)面向对象特征之一:封装和隐藏
- (10)类的成员之三:构造器(或构造方法)
- (11)JavaBean
- (12)UML类图
- (13)关键字—this
- (14)关键字—package
- (15)MVC设计模式
- (16)关键字—import
- 3、面向对象中
- (1)面向对象特征之二:继承性(inheritance)
- (2)方法的重写(override/overwrite)
- (3)关键字—super
- (4)子类对象实例化过程
- (5)面向对象特征之三:多态性
- (6)Object 类的使用
- (7)包装类(Wrapper)的使用
- 4、面向对象下
- 5、异常处理
- 6、多线程
- 7、常用类
- 8、枚举类与注解
- 9、集合
- 10、泛型与File
- 11、IO流与网络编程
- 12、反射
- 13、动态代理与Java8新特性
- 14、Java9&10&11新特性
- (二)Java设计模式
- (三)数据结构与算法
- 二、JavaWeb部分
- (一)基础内容
- (二)JQuery内容
- (三)Ajax内容
- 三、JavaEE基础部分
- (一)MyBatis内容
- (二)Mybatis Plus内容
- (三)Maven内容
- (四)Mapper内容
- (五)Spring5框架内容
- (六)Spring注解驱动内容
- (七)SpringMVC内容
- (八)SSM高级内容
- (九)Struts2内容
- 四、JavaEE高级部分
- 五、项目实战部分
- 六、Java面试部分
一、Java基础部分
(一)核心基础
1、常用算法
(1)二分法查找
前提:数组有序,每次折半查找
代码:
int[] arr=new int[]{1,3,5,6,9,12,23,25,46,67};
int goal=25;
int head=0;
int end=arr.length-1;
boolean flag=true;
while(head<=end){
int midden=(head+end)/2;
if(goal==arr[midden]){
System.out.println("找到指定元素了");
flag=false;
break;
}else if(goal>arr[midden]){
head=midden+1;
}else{
end=midden-1;
}
}
if(flag){
System.out.println("没有找到");
}
(2)冒泡排序
public class BubbleSortTest {
public static void main(String[] args) {
int [] arr=new int [] {43,231,56,8,98,-42};
// 只需元素个数-1轮排序
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]) {
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
}
(3)快速排序
public class QuickSort {
public static void swap(int [] data,int i,int j) {
int temp=data[i];
data[i]=data[j];
data[j]=temp;
}
public static void subSort(int[] data,int start,int end) {
if(start<end) {
int base=data[start];
int low=start;
int high=end+1;
while(true) {
//或者low<end
while(low<=high&&data[++low]<=base)
;
//或者high>start
while(high>=low&&data[--high]>=base)
;
if(low<high) {
swap(data,low,high);
}
else {
break;
}
}
swap(data,start,high);
subSort(data,start,high-1);
subSort(data,high+1,end);
}
}
public static void quickSort(int [] data) {
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
int []data=new int [] {43,231,56,8,98,1,463,26,-78,45,3723,-42};
System.out.println("排序前:"+java.util.Arrays.toString(data));
quickSort(data);
System.out.println("排序后:"+java.util.Arrays.toString(data));
}
}
(4) 算法复杂度
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | (时间复杂度(最好) | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 |
冒泡排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 |
快速排序 | O(nlog2n) | O(n2) | O(nlog2n) | O(nlog2n) | 不稳定 |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 |
(5)Arrays工具类的使用
Arrays.equals(int[]a,int[]b) :判断两个数组是否相等,返回boolean
Arrays.toString(int []a) : 输出数组信息 返回String
Arrays.fill(int []1,int val) : 将指定值填充到数组之中 无返回
Arrays.sort(int[] a) : 对数组进行排序 无返回
Arrays.binarySearch(int[]a ,int key) : 对有序的数组进行二分法检索指定的值
2、面向对象上
(1)Java面向对象学习的三条主线:
1、Java类及类的成员:属性、方法、构造器;代码块、内部类;
2、面向对象的三大特征:封装性、继承性、多态性、(抽象性);
3、其他关键字:this、super、static、final、abstract、interface、package、import;
(2)面向过程(POP) 与 面向对象(OOP)
二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的 是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对 象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如 抽象、分类、继承、聚合、多态等。
(3)面向对象的三大特征
封装 (Encapsulation)
继承 (Inheritance)
多态 (Polymorphism)
面向对象:Object Oriented Programming
面向过程:Procedure Oriented Programming
(4)面向对象的思想概述
- 程序员从面向过程的执行者转化成了面向对象的指挥者
- 面向对象分析方法分析问题的思路和步骤:
根据问题需要,选择问题所针对的现实世界中的实体。
从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。 - 类(Class)和对象(Object)是面向对象的核心概念。
类是对一类事物的描述,是抽象的、概念上的定义
对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
“万事万物皆对象”。
可以理解为:类 = 抽象概念的人;对象 = 实实在在的某个人
面向对象程序设计的重点是类的设计
类的设计,其实就是类的成员的设计
(5)Java类及类的成员
- 现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的细胞构成的。同
理,Java代码世界是由诸多个不同功能的类构成的。
现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质、… 那么,Java中用类class来描述事物也是如此。常见的类的成员有:
属 性:对应类中的成员变量
行 为:对应类中的成员方法
Field = 属性 = 成员变量,Method = (成员)方法 = 函数
(6)对象的创建和使用:内存解析
- 堆(Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象
实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的
对象实例以及数组都要在堆上分配。 - 通常所说的栈(Stack),是指虚拟机栈。虚拟机栈用于存储局部变量等。
局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、
char 、 short 、 int 、 float 、 long 、double)、对象引用(reference类型,
它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释
放。 - 方法区(Method Area),用于存储已被虚拟机加载的类信息、常量、静态
变量、即时编译器编译后的代码等数据。
引用类型的变量,只可能存储两类值:null或地址值(含变量的类型)
- 理解“万事万物皆对象”
1、 在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构。如Scanner、String;文件、File;网络资源、URL。
2、涉及到Java语言与前端html、后端数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。 - 匿名对象的使用
创建的对象没有显式的赋给一个变量名,即为匿名对象。
特征:匿名对象只能调用一次。
(7)类的成员之一:属性
-
语法格式:修饰符 数据类型 属性名 = 初始化值 ;
说明1: 修饰符
常用的权限修饰符有:private、缺省、protected、public
其他修饰符:static、final (暂不考虑)
说明2:数据类型
任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
说明3:属性名
属于标识符,符合命名规则和规范即可。 -
变量的分类:成员变量与局部变量
在方法体外,类体内声明的变量称为成员变量。
在方法体内部声明的变量称为局部变量。
注意:二者在初始化值方面的异同:
同:都有生命周期
异:局部变量除形参外,均需显式初始化。
成员变量 | 局部变量 | |
---|---|---|
声明的位置 | 直接声明在类中 | 方法形参或内部、代码块内、构造器内等 |
修饰符 | private、public、static、final等 | 不能用权限修饰符修饰,可以用final修饰 |
初始化值 | 有默认初始化值 | 没有默认初始化值,必须显式赋值,方可使用 |
内存加载位置 | 堆空间 或 静态域内 | 栈空间 |
- 对象属性的默认初始化赋值
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是引用类型,如上面的Person及前面讲过的数组。
成员变量类型 初始值
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
char 0 或写为:’\u0000’(表现为空)
boolean false
引用类型 null
(8)类的成员之二:方 法(method)
-
什么是方法(method、函数):
方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
将功能封装为方法的目的是,可以实现代码重用,简化代码
Java里的方法不能独立存在,所有的方法必须定义在类里。
比如Math类的sqrt()、random();Scanner类的nextXxx();Arrays类的sort()、binarySearch()、toString()、equals()。 -
方法的声明格式:
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){
方法体程序代码
return 返回值;
}
其中:
修饰符:public,缺省,private, protected等
返回值类型:没有返回值:void; 有返回值,声明出返回值的类型。与方法体中“return 返回值”搭配使用。
方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”
形参列表:可以包含零个,一个或多个参数。多个参数时,中间用“,”隔开
返回值:方法在执行完毕后返还给调用它的程序的数据。 -
方法的重载
重载的概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
重载的特点:与权限修饰符、返回值类型、形参变量名、方法体无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。 -
可变个数的形参
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String…books);
1.声明格式:方法名(参数的类型名 …参数名)
2.可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
3.可变个数形参的方法与同名的方法之间,彼此构成重载
4.可变参数方法的使用与方法参数部分使用数组是一致的
5.方法的参数部分有可变形参,需要放在形参声明的最后
6.在一个方法的形参位置,最多只能声明一个可变个数形参 -
方法参数的值传递机制
1、方法,必须由其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
2、Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参 -
递归(recursion)方法
递归方法:一个方法体内调用它自身。
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
(9)面向对象特征之一:封装和隐藏
-
我们程序设计追求“高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。 -
信息的封装和隐藏
Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
隐藏一个类中不需要对外提供的实现细节;
使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
便于修改,增强代码的可维护性; -
封装性的体现
1、如上;
2、不对外暴露的私有方法
3、单例模式 等 -
四种访问权限修饰符
Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
对于class的权限修饰只可以用public和default(缺省)。
public类可以在任意地方被访问。
default类只可以被同一个包内部的类访问。
可以修饰的内部结构:方法、属性、构造器、内部类
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
(10)类的成员之三:构造器(或构造方法)
-
构造器的特征
它具有与类相同的名称
它不声明返回值类型。(与声明为void不同)
不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值 -
构造器的作用:创建对象;给对象进行初始化
如:Order o = new Order(); Person p = new Person(“Peter”,15); -
语法格式:
修饰符 类名 (参数列表) {
初始化语句;
} -
根据参数不同,构造器可以分为如下两类:
隐式无参构造器(系统默认提供)
显式定义一个或多个构造器(无参、有参) -
注 意:
Java语言中,每个类都至少有一个构造器
默认构造器的修饰符与所属类的修饰符一致
一旦显式定义了构造器,则系统不再提供默认构造器
一个类可以创建多个重载的构造器
父类的构造器不可被子类继承 -
构造器重载
构造器一般用来创建对象的同时初始化对象。
构造器重载使得对象的创建更加灵活,方便创建各种不同的对象。
构造器重载,参数列表必须不同
构造器重载举例:
public class Person{
public Person(String name, int age, Date d) {this(name,age);…}
public Person(String name, int age) {…}
public Person(String name, Date d) {…}
public Person(){…}
}
- 现总结这几个位置,并指明赋值的先后顺序。
赋值的位置:
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过“对象.属性“或“对象.方法”的方式赋值
赋值的先后顺序:
① - ② - ③ - ④
(11)JavaBean
- JavaBean是一种Java语言写成的可重用组件。
所谓javaBean,是指符合如下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法 - 用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
(12)UML类图
- + 表示 public 类型,- 表示 private 类型,#表示protected类型
- 方法的写法:
方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型
(13)关键字—this
-
this是什么?
1、在Java中,this关键字比较难理解,它的作用和其词义很接近。
它在方法内部使用,即这个方法所属对象的引用;
它在构造器内部使用,表示该构造器正在初始化的对象。
2、this 可以调用类的属性、方法和构造器
3、什么时候使用this关键字呢?
当在方法内需要用到调用该方法的对象时,就用this。
具体的:我们可以用this来区分属性和局部变量。
比如:this.name = name; -
1、在任意方法或构造器内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性。不过,通常我们都习惯省略this。
2、当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加this来表明该变量是类的成员变量。
3、使用this访问属性和方法时,如果在本类中未找到,会从父类中查找。
4、this可以作为一个类中构造器相互调用的特殊格式。 -
注意:
1、可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器!
2、明确:构造器中不能通过"this(形参列表)“的方式调用自身构造器
3、如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了"this(形参列表)”
4、"this(形参列表)“必须声明在类的构造器的首行!
5、在类的一个构造器中,最多只能声明一个"this(形参列表)”
(14)关键字—package
- 1、package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。它的格式为:package 顶层包名.子包名 ;
2、包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次;
3、包通常用小写单词标识。通常使用所在公司域名的倒置:com.atguigu.xxx - 包的作用:
1、包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
2、包可以包含类和子包,划分项目层次,便于管理
3、解决类命名冲突的问题
4、控制访问权限
(15)MVC设计模式
- MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,与数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
- 模型层 model 主要处理数据
1、数据对象封装 model.bean/domain
2、数据库操作类 model.dao
3、数据库 model.db - 控制层 controller 处理业务逻辑
1、应用界面相关 controller.activity
2、存放fragment controller.fragment
3、显示列表的适配器 controller.adapter
4、服务相关的 controller.service
5、抽取的基类 controller.base - 视图层 view 显示数据
1、相关工具类 view.utils
2、自定义view view.ui
(16)关键字—import
- 为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。import语句告诉编译器到哪里去寻找类。
- 语法格式:
import 包名. 类名; - 注意:
1、在源文件中使用import显式的导入指定包下的类或接口
2、声明在包的声明和类的声明之间。
3、如果需要导入多个类或接口,那么就并列显式多个import语句即可
4、举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
5、如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
6、如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
7、如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
8、import static组合的使用:调用指定类或接口下的静态的属性或方法
3、面向对象中
(1)面向对象特征之二:继承性(inheritance)
-
为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)。可以理解为:“子类 is a 父类”
类继承语法规则:
class Subclass extends SuperClass{ } -
作用:
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。
注意:不要仅为了获取其他类中某个功能而去继承 -
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。 -
关于继承的规则:
子类不能直接访问父类中私有的(private)的成员变量和方法。 -
Java只支持单继承和多层继承,不允许多重继承
一个子类只能有一个父类
一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok
class SubDemo extends Demo1,Demo2…//error -
子类直接继承的父类称为直接父类,间接继承的父类称为间接父类。子类继承父类后,就获取了直接父类以及所有间接父类中声明的属性和方法。
-
如果没有显式的声明一个类的父类方法,则此类继承于java.lang.Object类,所有的java类,除java.lang.Object类之外,都直接间接的继承于java.lang.Object类。意味着所有的java类具有java.lang.Object类中的功能。
(2)方法的重写(override/overwrite)
- 定义
在子类中可以根据需要对从父类中继承来的方法进行改造,也称
为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。 - 要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型。
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void;
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类型或A类型的子类;
父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型可以是相同基本数据类型。 - 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限,子类不能重写父类中声明为private权限的方法
- 子类方法抛出的异常不能大于父类被重写方法的异常
- 注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
(3)关键字—super
- 在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器 - 注意:
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存
空间的标识 - 调用父类的构造器
1、子类中所有的构造器默认都会访问父类中空参数的构造器
2、当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
3、如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错 - this和super的区别
区别点 | this | super | |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 直接访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法则从父类中继续查找 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
(4)子类对象实例化过程
(5)面向对象特征之三:多态性
- 多态性,是面向对象中最重要的概念,在Java中的体现:
对象的多态性:父类的引用指向子类的对象
可以直接应用在抽象类和接口上 - Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法) - 对象的多态 —在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象 - 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向
上转型(upcasting) - 一个引用类型变量如果声明为父类的类型,但实际引用的是子类
对象,那么该变量就不能再访问子类中添加的属性和方法 - 多态性只适用于方法,不适用于属性。
- 正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下) 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法 - 编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定 - 方法的重载与重写
从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类
和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,
这称为“早绑定”或“静态绑定”; 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。” - 关键字instanceof 操作符
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。 - 对象类型转换 (Casting )
1、基本数据类型的Casting:
自动类型转换:小的数据类型可以自动转换成大的数据类型,如long g=20; double d=12.0f
强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型,如 float f=(float)12.0; int a=(int)1200L
2、对Java对象的强制类型转换称为造型
从子类到父类的类型转换可以自动进行
从父类到子类的类型转换必须通过造型(强制类型转换)实现
无继承关系的引用类型间的转换是非法的
在造型前可以使用instanceof操作符测试一个对象的类型
- 子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
(6)Object 类的使用
- Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类 - Object类中的主要结构
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public Object() | 构造 | 构造器 |
2 | public boolean equals(Object obj) | 普通 | 对象比较 |
3 | public int hashCode() | 普通 | 取得Hash码 |
4 | public String toString() | 普通 | 对象打印时调用 |
- ==操作符与equals方法
- ==
基本类型比较值:只要两个变量的值相等,即为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()方法。
当自定义使用equals()时,可以重写。用于比较两个对象的“内容”是否都
相等。 - 重写equals()方法的原则
对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是
“true”。
自反性:x.equals(x)必须返回是“true”。
传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你
重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”; x.equals(和x不同类型的对象)永远返回是“false”。 - ==和equals的区别
1 == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
2 equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
3 具体要看自定义类里有没有重写Object的equals方法来判断。
4 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
- toString() 方法
- 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);
(7)包装类(Wrapper)的使用
- 针对八种基本数据类型定义相应的引用类型—包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
- 基本数据类型包装成包装类的实例 —装箱
通过包装类的构造器实现:
int i = 500; Integer t = new Integer(i);
还可以通过字符串参数构造包装类对象:
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException - 获得包装类对象中包装的基本类型变量 —拆箱
调用包装类的.xxxValue()方法:
boolean b = bObj.booleanValue(); - JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
- 字符串转换成基本数据类型
通过包装类的构造器实现:
int i = new Integer(“12”);
通过包装类的parseXxx(String s)静态方法:
Float f = Float.parseFloat(“12.1”); - 基本数据类型转换成字符串
调用字符串重载的valueOf()方法:
String fstr = String.valueOf(2.34f);
更直接的方式:
String intStr = 5 + “”
CSDN-Ada助手: 非常感谢CSDN博主的分享,这篇《Java面试题汇总》对于准备Java面试的读者来说非常有帮助。我觉得下一篇博客可以继续深入Java相关技术,比如可以写一篇关于Java并发编程的文章,介绍常见的并发问题及解决方案,相信会对其他Java开发者有很大帮助。期待更多精彩的技术分享! 为了方便博主创作,提高生产力,CSDN上线了AI写作助手功能,就在创作编辑器右侧哦~(https://mp.csdn.net/edit?utm_source=blog_comment_recall )诚邀您来加入测评,到此(https://activity.csdn.net/creatActivity?id=10450&utm_source=blog_comment_recall)发布测评文章即可获得「话题勋章」,同时还有机会拿定制奖牌。
盼盼编程: 厉害了!顶!欢迎一回访起交流!