一、Java概述
1、Java语言特点
(1)面向对象
(2)简单好用
Java语言由C和C++演变而来,它省略了C语言中所有的难以理解、容易混淆的特性(比如指针),更加严谨、简洁、易使用。
(3)健壮性
具有安全检查机制,还具备了许多保证程序稳定、健壮的特性(强类型机制、异常处理、垃圾的自动收集等),有效地减少了错误,使得Java应用程序更加健壮。
(4)安全性
Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击,从而可以提高系统的安全性。
(5)平台无关性(跨平台性)
java语言编写的程序,一次编译后,可以在多个系统平台上运行。(不受计算机硬件和操作系统的约束)
Java平台无关性由Java 虚拟机(JVM Java Virtual Machine)实现。
Java语言跨平台(√),JVM跨平台(×)
Java能跨平台正是以JVM不跨平台为代价的。
(6)支持多线程
C++ 语言没有内置的多线程机制,必须调用操作系统的多线程功能来进行多线程程序设计。
而 Java 语言却提供了多线程支持。
多线程机制使应用程序在同一时间并行执行多项任务,
该机制使得程序能够具有更好的交互性、实时性。
(7)分布式(支持网络编程)
Java语言具有强大的、易于使用的网络能力,非常适合开发分布式计算的程序。
java中提供了网络应用编程接口(java.net),使得我们可以通过URL、Socket等远程访问对象。
(8)编译与解释共存
一定有一个工具,帮助我们将程序转化为计算机可以识别的对应到机器指令级别的二进制序列。
编译型语言(如C、C++):用这种语言写出的代码,首先通过编译器的编译,全部转化成目标代码(二进制可执行文件.exe),然后依次在操作系统中执行。
解释型语言(如JAVA、Python):转化一句,执行一句。
2、Java和C++区别
- 都是面向对象的语言,都支持封装、继承和多态
- Java不提供指针来直接访问内存,程序内存更加安全
- Java的类是单继承的,C++支持多重继承;
- 虽然Java的类不可以多继承,但是接口可以多继承。
- Java有自动内存管理机制,不需要程序员手动释放无用内存
3、Java语言的平台版本
JAVASE:Java Platform Standard Edition,标准版,完成 桌面应用程序 的开发,是其它两者的基础;
JAVAME:Java Platform Micro Edition,小型版,开发 电子消费产品和嵌入式设备,如手机中的应用程序;
JAVAEE:Java Platform Enterprise Edition,企业版,开发 企业环境下的应用程序,主要针对web应用程序开发;
4、JDK、JRE和JVM
1,JDK:
Java Development Kit,java的开发和运行环境
包括java的开发工具(如Javac、Javap)和JRE。
仅仅只供开发者在开发阶段使用的工具。javac:
是JDK自带的编译器。
负责的是编译的部分,当执行javac时,会启动 java 的编译器程序,对指定扩展名的.java文件进行编译,生成jvm可以识别的字节码文件,即class文件,java的运行程序。(将java文件转变为字节码文件)
与之区分:java:负责运行的部分,会启动 jvm,加载运行时所需的类库,并对class文件进行执行。Javap:
是JDK自带的反编译工具。
把字节码反编译为汇编代码,用于帮助开发中深入理解Java编译器的机制。
javap和java互逆。
JDK 8:
直到今天为止,国内绝大部分公司,所使用的JDK版本仍然是JDK 8。
为什么大家都用JDK 8? 商业公司 ->求稳
LTS: long term suppor (长期支持版本 )维护周期长、至少3年、稳定。
而短期支持版本:维护周期短 、半年、不稳定。
从JDK9开始每半年发布一次,在这些快速迭代的版本中 ,JDK8 -> JDK11 ->JDK17是长期支持版本。
2,JRE:
Java Runtime Environment,java程序的运行环境
包括java运行所需的核心类库+JVM。
JRE主要是给已经写好的Java程序使用,换句话说,Java程序要能在操作系统中运行,必须有JRE。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包。
3、JVM:
Java Virtual Machine,java虚拟机,用于保证java的跨平台的特性。
java语言是跨平台,jvm不是跨平台的。
5、字节码
Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机器。
这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。
编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由 解释器 来将虚拟机代码转换为特定系统的机器码执行。
在Java中,这种供虚拟机理解的代码叫做字节码(即Java源代码经过虚拟机编译器编译后产生的文件,扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机,使Java程序无须重新编译便可在多种不同的计算机上运行。
每一种平台的解释器是不同的,但是实现的虚拟机是相同的。
一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点,使Java程序运行时比较高效。
Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm(字节码由虚拟机解释执行)---->jvm中解释器(虚拟机将每一条要执行的字节码送给解释器)----->机器可执行的二进制机器码---->程序运行。
6、Oracle JDK(简称JDK) 和 OpenJDK 的对比
Sun公司2009年被Oracle收购之前,将JDK源代码开源,形成了OpenJDK。但是,在Sun公司开源JDK源代码的时候,其中右一部分源代码(小部分,非核心功能),因为版权问题,无法完全开源,就被其他有同样功能的,开源代码所替代。
OpenJDK中,只包含JDK中最最核心的功能,还有其他的一些第三方实现的功能,或者是插件,OpenJDK是JDK的极简版本。
关于OpenJDK和JDK它的源代码是有关系的:包含在OpenJDK源代码中的绝大部分代码和Oralce JDK一模一样,但Oracle JDK有更多的类和一些错误修复,而且Oracle JDK 比 Open JDK 更稳定,具有更好的性能。Oracle JDK会和Open JDK 保持同步。一旦Oracle JDK发现Open JDK中的一些bug,Oracle JDK在修复后,会把这些修复后的代码同步到Open JDK中。
IBM,Google,FaceBook、RedHat都从OpenJDK中拿到源代码,然后经过修改,增加一些特有功能。例如阿里巴巴自己的JDK版本DragonWell。
Oracle JDK版本将每三年发布一次,OpenJDK版本每三个月发布一次;
OpenJDK 是一个参考模型并且完全开源,Oracle JDK是OpenJDK的一个实现,不是完全开源的;
Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本;
7、Java开发环境配置
(1)path环境变量的配置:
path是配置Windows可执行文件的搜索路径,即扩展名为.exe的程序文件所在的目录,
用于指定DOS窗口命令的路径。
告诉操作系统到哪里去找javac可执行程序配置。
永久配置方式:
JAVA_HOME=%安装路径%\Java\jdk
path=%JAVA_HOME%\bin
临时配置方式:
set path=路径;
(2)classpath的配置:
Classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。
给jvm用,告诉jvm到哪里加载字节码文件配置。
如果没有定义环境变量classpath,java启动jvm后,会在当前目录下查找要运行的类文件;
如果指定了classpath,那么会在指定的目录下查找要运行的类文件。
二、基础语法
1、关键字、保留字、标识符
(1)关键字:被赋予特殊含义的词
组成关键字的字母全部小写。
高级的编辑器或者专门的开发工具中,关键字会有高亮效果;
(2)保留字:尚未使用,但以后版本可能会作为关键字使用的词,const、goto。
(3)标识符:自定义的词。如类名、变量名、函数名、文件名等。
由字母、数字、下划线“_”、美元符号“$”或者“¥”组成,首字符不能是数字。
大小写敏感。
命名规则:
包:
包类似于操作系统中以文件夹的形式组织文件。
在JAVA语言中,以包来组织JAVA中的类,不同包下可以包含同名的类名。
为了包名的唯一,通常以域名反转的方式来命名包,例:com.baidu.www。
多级包: 以域名(是唯一的)反转的方式来命名包,单词全部小写。
单极包: 一个全部小写的单词。如 test
类和接口:
单个:首字母大写,其余字母全部小写。如 Student
多个单词:多个单词的首字母均大写,其余字母全部小写 。如:JavaBasic 、MaxAge
变量和方法:
单个:所有字母小写。 如:value
多个单词:第一个单词首字母小写,从第二个开始,每个单词首字母大写。 如:intValue
常量:
所有单词字母都大写。如MAX
多个单词:单词之间用_来分割,如MAX_AGE
具体的Java开发规范文档:阿里巴巴Java开发手册(嵩山版)
2、注释
-
单行注释
格式:
// 注释文字
-
多行注释
格式:
/* 注释文字 */
-
文档注释
格式:
/** 注释文字 */
可用来生成网页格式的帮助文档。
多行和文档注释都不能嵌套使用。
3、数据类型及转换、进制、补码原码
(1)数据类型
①基本数据类型(原始数据类型)(4类8种):
整数类型:byte、short、int、long
浮点数类型:float、double
字符类型:char
布尔类型:boolean
②引用数据类型:
类、接口、数组
(2)类型转换
精度从高到低 double float long int short,char, byte
自动类型转换(隐式转换):低精度–>高精度
boolean 类型的值不能直接转化为其它数据类型的值。
short、char、byte相互之间不转换,他们参与运算会首先转换为 int 类型。
强制类型转换(显示转换): 高精度–>低精度(精度会下降)
举例:
byte b1 = 1, b2 = 2, b;①
b = b1+ b2;②
b= 1 + 2;③
哪句是编译失败的呢?为什么?
答:①③编译成功,②编译失败。
原因:②中计算b1+b2前先将b1和b2默认转换为int类型,计算结果int类型赋值给byte导致编译出错。
③中赋值的是1 + 2 ,是常量值,编译器简单的判断了一下,不会超过byte类型变量的表示范围。
如果③变成b= 1 + 202;则编译器报错。
举例:
byte b = 129; 这句代码有没有问题?如果有问题,是否有办法完成上述赋值功能?
答:有问题。byte的表示范围是-128~127,129超出byte表示范围,故无法完成赋值。
更改:(强制转换)byte b=(byte)129;
此时输出b为-127。(用补码计算)
(3)进制
基数:表示进位制所具有的数码的个数。
如十进制的基数为10,二进制的基数为2等。
十进制数:在数字的右下角标注10或D。默认是十进制。
二进制数:在数字的右下角标注2或B。
Java语言中0b开头。
八进制数:在数字的右下角标注8或字母O。
Java语言中数字0开头。
十六进制数:在数字的右下角标注16或H。
Java语言中0X开头。
进制之间的转换:
① 二进制 --> 十进制
将二进制数写成按权展开式,并将式中各乘积项的积相加,即得对应十进制数。
② 十进制 --> 二进制 / 八进制等
分为整数部分转换和小数部分转换
在十进制的小数部分转换中,有时连续乘以2不一定能使小数部分等于0,这说明该十进制小数不能用有限位二进制小数表示。这时,只要取足够多的位数,使其误差达到所要求的精度就可以了。
示例:十进制转换成二进制
③ 二进制 --> 八进制
④ 八进制 --> 二进制
⑤ 二进制 --> 十六进制
⑥ 十六进制 --> 二进制
(4)补码原码
正数的原码 = 补码
原码 --> 补码:符号位不变,其余位取反,末位 + 1。
补码的补码(补码求补)= 补码对应的原码。
补码中0只有一种表示方法 形式上 = 原码中的 正0。
补码比原码多表示一个负数:-2字长-1(规定)
所有整数在计算机中均是以补码形式存在的。
4、访问权限修饰符
修饰符 | 访问范围 | 是否能被子类继承 | 适用对象 |
---|---|---|---|
private | 本类内部 | 不能被继承 | 变量、方法 |
(default) | 本类内部 + 同包的其他类 | 能被同包的子类继承 | 类、接口、方法 |
protected | 本类内部 + 同包的其他类+非同包的子类 | 能被继承 | 变量、方法 |
public | 公开,所有类都能访问,最大权限修饰符 | 能被继承 | 类、接口、变量、方法 |
在Java语言中,一个Java文件中只能定义一个被pulic修饰的类,且被public修饰的类名,必须和Java文件的文件名相同。
5、运算符
(1)算数运算符
"+" 运算符:
可表示加法运算;
可表示正数;
可表示字符串拼接:
操作数1 + 操作数2 + … 在多个操作数中,只要有一个是字符串,"+“操作执行的就是字符串拼接。
举例:
System.out.println(“hello” + ‘a’ + 1); // helloa1
System.out.println(‘a’ + 1 + “hello”); // 98hello(‘a’的ascii码是97)
System.out.println(“5+5=”+5+5); //5+5=55
System.out.println(5+5+”=5+5");//10=5+5
(2)赋值运算符
符号:= , +=, -=, *=, /=, %=。
其中=为基本的赋值运算符,其他的为扩展的赋值运算符。
a+=b和a=a+b的区别:
①a += b执行实际过程:先计算出a的值,然后用一个temp对象存储,之后和b进行相加,然后将值赋值给a引用。
+= 如果两边的操作数的精度不一样时会自动向低转化。
②a = a+b 执行过程:先计算 a + b,然后再赋值给a引用,给a引用的时候如果 引用a 有计算过程,则会再次计算。
a = a+b则不会自动转化,需要手动进行强制类型转化。
举例:
short s1 = 1; s1 = s1 + 1;(x)与short s1 = 1; s1 += 1;(√)
对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。
而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。
(3)关系运算符
关系运算符的结果只有两种true or false。
(4)逻辑运算符
用于连接boolean类型表达式。
&:逻辑与,只有两边都为true结果是true。否则就是false。(无论左边结果是什么,右边都参与运算)
&&:短路与,如果左边为false,那么右边不参数与运算。
|:逻辑或,只要两边都为false结果是false,否则就是true。(两边都参与运算)
||:短路或,如果左边为true,那么右边不参与运算。
^:异或:两边结果一样,为false,不一样,为true。
(5)位运算符
位运算符 | 描述 | 实例 |
---|---|---|
& | 按位与 | 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0 |
| | 按位或 | 两个相应的二进制位中只要有一个为1,该位的结果值为1 |
^ | 按位异或 | 同0异1 (若参加运算的两个二进制位值相同则为0,否则为1。)0和任何数做异或运算为此数本身 ,任何数与它自己做亦或为0. |
~ | 按位取反 | ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1 |
<< | 左移 | 用来将一个数的各二进制位全部左移N位,右补0。相当于对操作数乘2(前提是移位后结果仍然在表示范围内) |
>> | 右移 | 用来将一个数的各二进制位全部右移N位,左补符号位。相当于给操作数除以2(只针对未溢出且可以除尽的情况) |
>>> | 无符号右移 | 用来将一个数的各二进制位全部右移N位,左补0。通常用来处理正整数,对正整数而言相当于除以2 |
举例:见 位运算符举例 文章
6、一维数组
-
数据类型 [ ] 数组名 = new 数据类型 [ 长度 ] ;
-
数据类型 [ ] 数组名 = { 数据,数据,…,数据 } ;
-
数据类型 [ ] 数组名 = new 数据类型 [ ] { 数据,数据,…,数据 };
二维数组同理。
7、关键字
(1)final关键字
①修饰类
称为最终类。
不可被继承,final 类中的所有成员方法都会被隐式的指定为 final 方法,
②修饰方法
称为最终方法,不可被覆盖
③修饰变量
称为常量,只能赋值一次.
如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
如果是引用类型的变量,则在对其初始化之后便不能让其指向另一个对象,但是可以改变引用指向的内容。
内部类只能访问被final修饰的局部变量。
(2)this关键字
本质上是一个指向本对象的指针。
不可以在static环境中使用。包括:static变量,static方法,static代码块。
this关键字的用法:
①
super.属性名、super.方法名();
表示本类类型的对象引用.
用于区分同名成员变量和局部变量 或者 在定义函数时,该函数内部要用到调用该函数的对象时,但此时对象还没建立,用this代表此对象。
②
super([参数列表])
注意:用this调用构造函数,必须定义在构造函数的第一行。
public class TestThis{
int i = 2;
public TestThis(){
System.out.println("这是无参构造方法");
}
public TestThis(int i){
this(); //通过this()调用无参构造方法。
this.i = i;
}
public void test(){
int i = 3;
//局部变量与成员变量同名,成员变量将被隐藏
System.out.println(i);
//通过this.成员变量名 访问被隐藏的成员变量
System.out.println(this.i);
}
}
(3)super关键字
super()和this()不可以同时出现的构造函数中。
是指向直接(离自己最近的一个)超(父)类对象的指针。
不可以在static环境中使用。包括:static变量,static方法,static代码块。
super关键字的用法:
①
super.属性名、super.方法名();
表示指向当前对象的父类的引用。
用于区分同名子类成员和父类成员,用super。
②
super([参数列表])
调用的是父类中的对应参数的构造函数。
注意:用super调用构造函数,必须定义在构造函数的第一行。
(4)static关键字
①静态变量
修饰成员变量。
特点:
1)随着类的加载而加载。
2)优先于对象存在。
3)对所有对象共享,在内存中只有一个副本。
4)可以被类名直接调用。
5)初始化顺序按照定义的顺序进行。
调用格式:
类名.静态变量名
②静态方法
修饰成员方法。
特点:只能访问静态成员,不可以访问非静态成员。
原因:静态内容是随着类的加载而加载,先进内存。
非静态成员必须依赖具体的对象才能被调用。
用法:当某个方法没有访问该类中的非静态成员,就可以把这个方法定义为静态修饰。
调用格式:
类名.静态方法名()
③静态代码块、构造代码块
静态代码块:
定义在类中, 可以完成类的初始化(如初始化一些静态变量)。
随着类的加载而执行,只执行一次,因此可以用来优化程序性能。
常将只需要进行一次的初始化操作都放在static代码块中进行。
如果和主函数在同一类中,则优于主函数执行。
一个类中可以有许多静态初始化块,并且可以出现在类体的任何地方。
运行时系统会保证静态初始化块会按照它们在源代码中出现的顺序被调用
构造代码块:
给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块,只要对象一建立,就会调用。
静态代码块、构造代码块、构造函数同时存在时的执行顺序:
静态代码块 -->构造代码块 --> 构造函数
8、流程控制语句
① switch后的表达式的结果类型:byte、short、int、char
JDK7以后可以是:String字符串
② 跳出多重嵌套循环:
在外面的循环语句前定义一个标号(标签、标识符),然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。
没有标号的话,break;只终止一层循环。
举例1:
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++) {
System.out.print("i=" + i + ",j=" + j + " ");
if (j == 5){
System.out.println();
break;
}
}
}
结果:
举例2:(二重循环break)
public static void main(String[] args) {
flag:
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++) {
System.out.print("i=" + i + ",j=" + j + " ");
if (j == 5)
break flag;
}
}
结果:
举例3:(三重循环break)
public static void main(String[] args) {
flag:
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) //j = 1 break flag;
for (int k =0;k < 3;k ++) {
System.out.print("i=" + i + ",j=" + j + "," + "k=" + k + " ");
if (j == 1)
break flag;
}
}
结果:
举例4:(二重循环continue)
使用continue语句同理。
public static void main(String[] args) {
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if( j == 1)
continue outer;//此时continue结束的是outer表示的外层循环,此效果和break效果相同
System.out.print("i=" + i + ",j=" + j + " ");
}
}
}
结果:
举例5:(三重循环continue)
public static void main(String[] args) {
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
for (int k = 0;k < 3;k ++) {
if( j == 1)
continue outer;//此时continue结束的是outer表示的外层循环,此效果和break效果相同
System.out.print("i=" + i + ",j=" + j + "," + "k=" + k + " ");
}
}
}
结果:
9、可变参数
举例:
public class Test {
//可变参数是指方法在接受参数的时候,个数是不确定的。
//可变参数类型必须作为参数列表的最后一项,而不能放在定长参数的前面。
//编译器会把这个不确定的形参转化成一个数组形参,并在编译出的class文件里作上一个记号,表明这是个实参个数可变的方法。
public static void dealArray(int... intArray){
for (int i : intArray)
System.out.print(i +" ");
System.out.println();
}
public static void main(String args[]) {
//可变参数可以没有参数,即可以是空参数
dealArray(); //dealArray(int[] intArray{});
dealArray(1); //dealArray(int[] intArray{1});
dealArray(1, 2, 3); //dealArray(int[] intArray{1, 2, 3});
}
}
结果:(第一行为空)
1
1 2 3
三、面向对象
1、三大特征
特征1:封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
将变化隔离;方便使用;提高复用性;提高安全性
特征2:继承
用extends关键字表示
多个具体的对象,不断的向上抽取共享的内容,最终形成了一个体系。这个体系叫做继承体系。
java不支持多继承(一个类同时继承多个父类),但支持多重继承(A继承B B继承C C继承D)
特征3:多态
一个对象在程序不同运行时刻代表的多种状态,父类或者接口的引用指向子类对象。
通过多态可以提高代码的可重用性,降低模块之间的耦合度。
(1)Java实现多态的两种形式
①继承
多个子类对同一方法的重写。
继承是多态的前提。
②接口
实现接口并覆盖接口中同一方法。
(2)Java实现多态的三个必要条件:继承、重写、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能有 调用父类的方法和子类的方法 的能力。
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
2、类与对象
类变量的声明:
类名 对象名;
如:Student stu;
类对象的创建,赋值给变量:
对象名 = new 构造器([参数列表]);
如:stu = new Student();
类是创建对象的模板,由属性和行为两部分组成。
类是对象的概括或者抽象,对象是类的实例化。
成员变量(实例变量):对应事物的属性(成员函数对应事物的行为)。
匿名对象:应用于只调用一次类中的方法或作为实际参数在方法传递中使用
(1) 在子类方法中使用一个变量
首先,在方法的局部变量中找这个变量,有则使用。
否则,在本类中找成员变量,有则使用。
否则,在父类中找成员变量,有则使用。
否则,报错。
如果想要调用父类中的属性值,需要使用一个关键字:super(子类所属的父类中的内存空间引用)
(2)在子类对象使用一个方法
首先,在子类中找这个方法,有则使用。
否则,在父类中找这个方法,有则使用。
否则,报错。
(3)对象的加载过程:
加载父类,为父类静态变量分配内存
加载子类,为子类静态变量分配内存
执行父类静态变量的赋值运算,和静态初始化块
执行子类静态变量的赋值运算,和静态初始化块
(4)子类的实例化过程
① 子类创建对象时,会先去创建父类的对象,为父类实例变量分配内存
② 新建子类实例,为子类实例变量分配内存
③ 执行父类的实例变量赋值运算
④ 执行父类的构造方法
⑤ 执行子类的实例变量赋值运算
⑥ 执行子类的构造方法
子类构造函数运行时,先运行父类的构造函数。
因为子类的所有构造函数中的第一行有一条隐身的语句super();(调用父类中空参数的构造函数)
如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。
Person p = new Person();在内存中做了哪些事?
① 将Person.class文件加载进内存中。
② 如果p定义在主方法中,那么,就会在栈空间开辟一个变量空间p。
③ 在堆内存给对象分配空间。
④ 对对象中的成员进行默认初始化。
⑤ 对对象中的成员进行显示初始化。
⑥ 调用构造代码块对对象进行初始化。(如果没有就不执行)
⑦ 调用构造方法对对象进行初始化。对象初始化完毕。
⑧ 将对象的内存地址赋值给p变量,让p变量指向该对象。
3、变量区别
变量:程序运行期间可以被改变的量。
在程序中使用变量,必须先创建它并为它取一个名字,并且指明它能够存储信息的类型,称为“变量声明”,也叫容器的创建。
成员变量(实例变量) | 局部变量 | 静态变量(类变量) | |
---|---|---|---|
作用域 | 整个类 | 某个范围 | |
存储位置 | 堆内存 | 栈内存 | 方法区中的静态区 |
生命周期 | 随着对象的产生而存在,消失而消失 | 随着所属区域的运行而存在,结束而释放 | 随着类的加载而存在,消失而消失 |
默认初始值 | 有 | 没有默认初始值,使用前必须赋值 | |
调用 | 只能被对象调用 | 可被对象或类名调用 |
4、方法覆盖(复写,重写)override
方法重写(override):
实现的是运行时的多态性(也称为后绑定)。
子类从父类继承的某个实例方法无法满足子类的功能需要时,需要在子类中对该实例方法进行重新实现。
在不同类中(子父类中),方法声明相同(返回类型,方法名,参数列表均相同)。
要求:子类方法的访问权限要大于等于父类方法的访问权限,并且静态只能重写静态。
5、构造器(构造函数、构造方法)
作用:给对应的对象初始化。
所有对象创立时,只有初始化才可以使用。(有默认无参构造函数)
特点:
1)函数名与对应的类名相同
2)无返回值,但不能用void声明构造函数
3)生成类的对象时自动执行,无需调用
定义一个无参空函数体的构造器的作用:
Java程序在执行子类的构造方法之前,如果没有显式的用super()调用父类特定的构造方法,则会直接调用父类中没有参数的构造方法。
因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,
因为Java程序在父类中找不到没有参数的构造方法可供执行。
解决办法是在父类里加上一个不做事且没有参数的构造方法。
6、抽象类abstract
抽象类的声明:
[修饰符] abstract class 类名 [extends 父类名]{类体}
抽象方法的声明:
[修饰符] abstract 返回值类型 方法名([参数列表]);
抽象方法无法确定具体执行功能,没有方法体,需要在小括号后加上分号。
所有的对象都是通过类来描述的,但不是所有的类都用来描述对象。
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
抽象方法仅为了不让该类创建对象。只能定义在抽象类中,并且只定义方法声明,不定义方法实现,不可以被创建对象(实例化对象)。
只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化(抽象类只有被继承才可使用)。否则,该子类还是一个抽象类。
当有一部分内容是不想让子类修改的,但是子类又都通用,同时各自又有其特点,那么就适合使用抽象类。
抽象类可以定义实例变量和静态变量以及常量
抽象类可以再继承抽象类,也可以继承普通的类
7、接口interface
用interface关键字表示。类与接口关系用implements表示。
(Implements代替extends,Interface 代替class)
接口的声明:
[修饰符] interface 接口名{[常量];[抽象方法];}
接口的实现:
[修饰符] class 类名 [extends 父类名] [implements 接口1,接口2,……]{类体部分}
接口的特点:
1)接口只能定义抽象方法
2)接口只能定义常量
3)接口只能继承接口,不能继承普通的类和抽象类
4)接口没有构造方法
接口的作用:
1)提高程序的重用性
2)提高程序的可扩展性
3)降低程序的耦合度
4)实现了多继承
注意:
1)在接口中定义常量时,可以不用final static修饰,因为编译器在编译时会自动加上。
2)在接口中定义抽象方法时可以省略abstract关键字,编译器在编译时同样会加上。
(接口中的成员只能是常量和抽象方法。)
接口和抽象类区别
抽象类 | 接口 | |
---|---|---|
定义 | 描述子类的通用特征,是类的抽象,是一种模板设计 | 抽象方法的集合,是行为的抽象,是一种行为模型 |
声明 | 用abstract声明 | 用interface声明 |
实现 | 子类用extends关键字继承。如果子类不是抽象类,则需要提供抽象类中所有声明的实现 | 子类用implements关键字实现,需提供接口中所有声明的方法的实现 |
构造器 | 有 | 无 |
访问修饰符 | 任意 | 默认public,不能是private或protected |
成员变量 | 变量或常量 | 常量,默认修饰 public static final |
成员方法 | 抽象方法或非抽象方法 | 抽象方法,有默认修饰 public abstract |
继承 | 一个类最多只能继承一个抽象类(只能被单继承) | 一个类可以实现多个接口 |
字段声明 | 任意 | 默认是static和final |