目录
前言
Java注解
1)元注解
元注解(meta-annotation):负责注解其他注解(annotation)。
Java定义了4个标准的元注解类型:
a.@Target
b.@Retention
c.@Documented
d.@Inherited
(这些类型和它们所支持的类在java.lang.annotation包中)
a.@Target:
其说明了注解所修饰的对象范围:注解可用于包、类、接口、成员变量、构造方法、方法、方法参数、局部变量等。
@Target(Element.Type)
①CONSTRUCTOR:用于描述构造器
②FIELD:用于描述域
③LOCAL_VARIABLE:用于描述局部变量
④METHOD:用于描述方法
⑤PACKAGE:用于描述包
⑥PARAMETER:用于描述参数
⑦TYPE:用于描述类、接口
b.@Retention:
其定义了注解被保留的时间长短:某些注解仅出现在源代码中,在编译时被丢弃;而另一些则被编译在class文件中;编译时一般会被虚拟机所忽略,@Retention表示要在什么级别保存注释信息并保存多久。
@Retention(RetentionPoicy)
①SOURCE:在源文件中保留
②CLASS:在类文件中保留
③RUNTIME:在运行时保留
c.@Documented:
其用来描述其他类型的注解应该被作为被标注的公共API(公共的应用程序访问点,可通过该注解访问数据库中的其他数据)。
d.@Inherited:
其是一个标记注解,阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的注解类型被用于一个类,则这个注解将会被用于这个类的子类。(注意:实现类中不会继承接口的注解,重载方法中不会继承方法中的注解)
Java概述
1)Java的三种技术架构
a.JavaSE:完成桌面应用程序的开发,是技术架构的基础。
b.JavaEE:开发企业环境下的应用程序,主要针对web开发。
c.JavaME:开发电子产品和嵌入式的设备。
2)Java开发和运行环境
a.JDK:主要包括Java的开发工具包与基本开发环境。
b.JRE:主要包括Java的程序运行环境与运行所需的类库。
c.JVM:Java虚拟机,Java程序运行在虚拟机上,再被系统调用。
3)Java命令与编译
Java命令分为:编译时、运行时。
注意:Java以类为单位编译,不是以文件为单位编译。
a.Javac 负责编译部分:当执行Javac命令时,会启动编译器;对指定.java文件进行编译。生成虚拟机可以识别的.class文件,也就是Java的运行程序。jar是打包工具。
b.Java 负责运行部分:当执行Java命令时,会启动虚拟机加载运行时所需要的类库,并对.class文件进行执行。(程序执行的起始点为main方法)
注意:当使用Java命令执行一个类的时候,会首先通过CLASSPATH找到指定的路径,而后在此路径下加载虚拟机能运行的.class文件。
Java语法基础
1)命名规则
a.包名:包名通常是英文小写。约定俗成为公司的倒写域名加项目名。
导入包需要用关键字:import。
b.类名:通常以大写字母开头。
c.Java对大小写敏感
d.方法名通常首单词字母小写,后面的单词首字母大写
2)关键字
关键字是有特殊意义和作用的单词。
保留字也是一种关键字,不过还没有赋予特殊含义,但是准备未来被使用。
3)标识符
标识符是在程序中自定义的名词。如变量名、方法名等,包含0-9、a-z、A-Z、_、$。(注意不能使用数字开头,不能使用关键字和保留字)
4)常量
常量是程序中不会变化的数据。常量通常大写。
5)变量
变量其实是内存中的一个存储空间,用于存储数据。该存储空间的标记就是该变量的变量名。(该存储空间是可以重复使用的)
a.变量存储空间的开辟:
变量存储空间的开辟需要声明这个空间要新存储的数据的数据类型,还可以更新变量名。第一次存入该变量存储空间的数据需要赋初始值(初始化)。
b.变量的作用域和生存期:
变量的作用域:作用域是从变量定义的位置开始到该变量所在的末尾大括号结束。括号外的变量可以作用于括号内的程序块,反之不行。
变量的生命周期:变量从定义的位置就开始在内存中存活,执行超过作用域时,变量从内存中消失。
6)基本数据类型与引用数据类型
基本数据类型:
a.整数:byte字节型(占内存1个字节:8位)、short短整型(占内存2个字节:16位)、int整型(系统默认型,占内存4个字节:32位)、long长整型(占内存8个字节:64位)。(注意系统默认整数变量为int型,若要定义为其他的类型需要在末尾加入其他类型的首字母:如Long L=4L。但如果需要定义的数据没有超过该类型定义的内存空间则无需添加。其次,若数据超出了其定义的类型的存储范围,会执行取模操作或直接报错)
注意:①byte b=3;
b=b+2;//报错,因为2默认是int值。
②有long类型时,做算术运算,其他数据类型会自动转换成long型;char类型做算术运算,会自动转换成int型。
b.浮点数:float(单精度占,4个字节)、double(双精度,系统默认型,占8个字节)(注意系统默认浮点数变量为double,若要定义为其他的类型需要在末尾加入其他类型的首字母:如float f=0.5F。其次单精度的数可以定义类型为double;但双精度的值不能定义为float,会损失精度)
c.字符型:char(使用’’),可以定义数字(对应ASC码,65为A,97为a,48为0)、单词、汉字。Java使用Unicode字符集。字符型的默认值为’\u0000’
d.布尔型:boolean(只有true和false两种状态)
boolean类型变量可能占1位或4个字节。
当占用1位时:boolean类型的值只有true和false两种逻辑值,在编译后会使用0和1来表示,这两个数在内存中只需要1位即可存储。
当占用4个字节时:boolean a=true//这个a在内存中占用32位。
布尔数的默认值为:false
引用数据类型:
a.字符串类型:String(使用" "),可以定义字符串。是不可变字符串。已经定义的成员不可以改变状态。
String是封装char[]数组的对象。
表现形式:char[] a={'a','b','c','d};
String s=new String(a);
字符串的默认值为:null
①字符串中的equals和==:
==比较内存地址。String类重修了父类Object类的比较方法,equals用来比较字符内容。
②字符串常用方法:
charAt(i):获取指定位置的字符。
length():字符串长度,字符的数量。
indexof(子串):找第一个子串出现的初始位置,找不到返回-1。
indexof(子串,start):指定位置开始找子串,找不到返回-1。
lastIndexof(子串):倒序找出子串的位置。
subString(start):截取start到末尾的字符串。
subString(start,end):截取start到end的字符串。
trim():去除两端的空白字符。
③StringBulider:可变的字符序列,封装char[]数组。 提供了一组方法,可以对内部封装的字符进行修改,常用来代替字符串做高效的操作。
方法:
append():追加字符内容。
delete(start,end):删除区间。
deleteCharAt(i):产出指定位置i的元素。
insert(i,内容):在指定位置插入内容。
insertCharAt(i,字符):在指定位置插入单个位置。
replace(start,end,内容):替换指定范围的内容。
④StringBuilder和StringBuffer的区别:
StringBuilder:线程不安全,效率高。
StringBuffer:线程安全,效率低。
b.引用类型:[类名] [对象名]
引用变量中保存这一个实例的内存地址。
c.类型转换
类型的级别:(从低到高)
Char、byte、short->int->float->long->double
1)自动转换类型:从低级别到高级别系统自动转换。
2)强制转换类型:从高级别到低级别需要手动转换。
如:int i=(int)1.5;
a.运算符号
b.算数运算符:+、-、*、/、%(任何数模2只有1和0两种结果,可以用来实现开关算法)
c.赋值运算符:=、+=、-=、*=、/=、%=
a+=b等于a=a+b,以此类推。
d.比较运算符:>、<、>=、<=、!==
特点:返回值为true或false。返回值的快捷键(Shift+Alt+L)
特殊:三元表达式
布尔表达式?a:b;若布尔表达式为true,则结果为a;布尔表达式为false,则结果为b。
int i=1;
i=i>0?10:-10;
e.逻辑运算符:&、|、!、^、&&、||
处理!以外,其他都用于连接两个布尔表达式。
^:相同返回0(false),不同返回1(true)。
&和&&的区别:&:无论左边是什么内容,右边都会参与运算。&&短路与:当左边为false时,不会执行右边的表达式,直接返回结果为false。
|和||的区别:|:无论左边是什么内容,右边都会参与运算。||短路或:当左边为true时,不会执行右边的表达式,直接返回结果为true。
f.位运算符:操作二进制数:&、|、!、~、>>、<<
~:按位取反。
<<:左移,相当于乘以2。
>>:右移,空出来的高位用0补充。
(注意:二进制数开头的数还用于确定正负,1为负0为正)
字面值前缀:0b二进制,0八进制,0x十六进制。
7)语句
系统输出语句:
System.out.println();//输出后换行
System.out.println();//输出后不换行
Java语句有if语句、switch语句、do语句、while语句、for语句。
a.这些语句什么时候用?
当判断固定个数的值的时候,可以用if或switch,但是switch语句效率较高。
switch(变量){
case 值:要执行的语句;break;
//case具有传递性,break打断传递性
...
default:要执行的语句;
}
switch语句原理:用小括号中的变量依次与程序块中的case的值进行比较,和哪个值相同就执行哪个case后面所有的语句(传递性),如果没有相同的就执行default语句。
细节注意:①break可以省略,但一省略就会执行之后所有的语句。
②switch后面小括号的中的变量数据类型应该是byte、char、short、int四种类型中的一种。
③default可以写在switch结构中的任何位置,但要注意在default后面加break。
b.当判断数据范围时,获取判断运算结果boolean类型时,需要用if语句。
c.当某些语句需要执行很多次时,就用循环结构while或for。如果需要定义变量控制循环,建议使用for;for循环结束后,变量在内存中释放。
直接打印是打印数组的地址。
====================================================
增强版for循环遍历数组元素。结果:
====================================================
循环中出现return可能会报错,因为for中定义的是变量,变量只有在运行时才赋予地址,在编译时编译器无法判断for中的判断语句是否为true,从而读取不到返回值。
解决方法:把声明的变量改成常量。
d.break和continue
break:作用于switch和循环语句,用于结束循环
注意:break语句单独存在时,下面不要定义其他语句,因为会执行不到,编译会失败。当循环嵌套时,break只跳出当前所在的循环。
continue:只作用于循环结构,结束本次循环直接进行下一轮循环。
注意:continue下面不可以定义语句,执行不到。
a.break也可以在后面接标签,用来结束一些嵌套比较复杂的循环:
flag:
for(...){
for(...){
break flag;//结束循环
}}
b.continue也可以在后面接标签,在嵌套循环中跳出本次循环:
flag:
for(...){
for(...){
continue flag;//跳出本次循环,直接进入下次循环
}}
8)方法
方法:为了提高代码的复用性,可以将其定义成一个单独的功能。
a.Java中的方法的定义格式
修饰符 返回值类型 方法名(参数类型 形式参数){
执行语句;
return 返回值;//也有结束方法的作用。
}//没有返回值时,方法的返回值类型为void。
//注意:一般方法的修饰符都会有static关键字。
static关键字:
①static修饰的成员变量和成员方法随着类的调用被调用。
静态成员可以使用类名.直接访问,也可以通过对象访问。
②static修饰的成员变量和成员方法优先于对象。
③static修饰的成员变量和成员方法可以被所有对象所调用。
注意:static修饰的成员属于类,对象和实例方法不能用static修饰,而且static修饰的成员只能实例化一次。
如果系统在静态方法(如main方法)中调用非静态成员,可以通过创建类的对象,通过对象来调用。在普通成员方法中,可以直接访问同类的非静态变量和静态变量。
④static不能修饰局部变量。
b.如何定义一个方法?
方法是一个功能,定义方法就是实现功能。要明确该功能运算完的结果,确定返回值的类型。还要考虑这个方法的参数列表。
注意:一般方法如果使用了void方法声明,理论上此方法不能够有返回值,但是却可以通过调用return语句结束方法的调用。(即:return之后的程序不再执行)。
重载:在一个类中,如果出现了两个或者两个以上的同名方法,只要它们的参数的个数或者顺序、类型不同,就可称为重载,其只与参数列表有关。
c.从控制台输入数据
Scanner input=new Scanner(system.in)
Int i=input.nextInt();//也可以获取其他数据类型的数据。
d.方法的递归
9)数组
数组是用于存储同一类型数据的容器(连续的存储空间)。可以对容器中的数据(元素)进行编号,从0开始。
数组的表现形式:
a.静态方式
元素类型[] 数组名={元素1,元素2,...};
b.动态方式
元素类型[] 数组名=new 元素类型[元素个数];
此时:数组名.length=元素个数;
数组名[元素编号]=数值;
c.动静结合方式
元素类型[] 数组名=new 元素类型[元素个数]{元素1,元素2,...};
二维数组的表现形式:int[][] arr=new int[10][10];
注意:Java中多维数组的声明和初始化应按从高维到低维的顺序进行:
Int[][] a=new int[3][];
//定义一个三行(由三个一维数组组成)的二维数组
a[0]=new int[2];//第一行(一维数组)有两个元素
a[1]=new int[4];//第二行(一维数组)有四个元素
a[3]=new int[8];//第二行(一维数组)有八个元素
//这是一个三行八列的二维数组
d.Arrays数组工具
Arrays.toString(数组):把数组数据连接成字符串。
Arrays.sort(数组):数组排序
Arrays.binarySearch(数组,目标值):二分查找法,在有序数组中查找目标值下标。
Arrays.copyof(数组,长度):复制数组成一个指定长度的新数组。
System.arraycopy(原数组,原数组起始位置,目标数组,目标数组起始位置,复制的数量):复制数组到已经存在的目标数组。可指定起始位置和长度。
e.数组的7种算法
①遍历
②求和
③求极值
④查找
⑤倒序
⑥排序(冒泡算法)
⑦插入
package java_basic;
import java.util.Arrays;
import java.util.Scanner;
public class ArraysInsert {
public static void main(String[] args) {
int[] arr= {1,2,3,4,5,6,7,8,9,10};
System.out.println("数组的长度为:"+arr.length);
Scanner input=new Scanner(System.in);
System.out.print("插入的元素:");
int num=input.nextInt();
System.out.print("插入的下标:");
int index=input.nextInt();
if(index<0) {
System.out.println("下标出错!");
return;
}else {
arraysInsert(arr,num,index);
}
}
public static void arraysInsert(int[] arr,int num,int index) {
if(arr.length<0) {
System.out.println("数组出错");
return;
}else {
int[] arrs=Arrays.copyOf(arr, arr.length+1);
for(int i=arrs.length-1;i>index;i--) {
arrs[i]=arrs[i-1];
}
arrs[index]=num;
System.out.print("新数组为:");
for(int e:arrs) {
System.out.print(e+" ");
}
}
}
}
10)堆、栈、队列三种存储方式的区别
a.堆:随意进出。
b.栈:先进后出。
c.队列:先进先出。
11)JVM内存空间分配
JVM内存被分为:程序计数器、虚拟机栈、本地方法栈、堆、方法区。
栈:存储的都是局部变量。只要运算完之后就会被释放。
堆:用于存储数组和对象实体。实体用于封装多个数据。
a.每个实体都有内存首地址值。
b.堆内存中的变量都有默认初始值:不同的数据类型初始值不一样。
c.堆具有垃圾回收机制:对内存中已经死亡或者没有长时间使用的对象进行清理。
注意:普通类型的变量在栈中直接保存它所对应的值,而引用类型的变量保存的是一个指向堆区的指针(地址),通过这个指针可以找到堆区中对应的对象。因此,普通类型变量只在堆区占用一块内存,而引用类型变量要在栈区和堆区都占一块内存。
方法区是各线程共享的内存区域,它用于存储已被虚拟机加载的类、常量、静态变量等信息。
面向对象思想
类:模板与图纸。
对象:按照模型与图纸实现的具体实例。
注意:可以用instanceof判断类与对象的关系:
a instanceof b;//若a是b的一实例(或者元素),则整个表达式的结果是true,否则结果为false。
类的加载过程:
加载:将class二进制字节流加载到内存中。
验证:包括文件格式、元数据、字节码的验证等。
准备:为类成员分配内存,设置初始值。
解析:加载这个类所引用的其他所有类。
初始化:初始化类变量或其他资源。是执行方法的过程。
注意:
a.类加载阶段不会涉及构造器的调用,那时不一定有对象的产生。
b.类变量在准备阶段分配内存并赋初始值(0),在初始化阶段才赋真实值。
c.方法实际上是JVM收集所有类变量的赋值语句和静态语句形成的。
d.两个类是否相等的前提是,都由一个类加载器加载。若不是,则一定不相等。
e.初始化某类数组是在准备阶段进行的,不会导致该类进入初始化阶段。
f.访问经static和final共同修饰的字段为编译期常量,不会导致该类进入初始化阶段
对象:是将方法等一些内容的封装。是从模板中创建的具体实例。
1)成员:
类中定义的变量为成员变量:对应着对象事物的属性。
类中定义的方法为成员方法:对应着对象事物的行为。
定义类就是在定义成员变量和成员方法。
类中的属性通常被私有化封装:
private关键字//访问权限最低,只能在本类中被访问。
private关键字修饰的成员:其他类不能直接创建对象进行访问。
2)main方法:程序的入口。
3)成员变量与局部变量(方法中定义的变量)的区别
a.成员变量定义在类中,而局部变量定义在方法中、参数和语句上。
b.成员变量通常私有,只在本类中有效。而局部变量只在自己所属的大括号的作用域中有效。
c.成员变量随着对象存在于堆内存中,局部变量存在于栈内存中。
d.局部变量必须初始化,局部变量的作用域在定义.
e.两类变量同名时,局部变量的优先级更高。
4)构造方法
构造对象的方法,用于给对象初始化。
特点:a.构造方法的方法名和类名相同。
b.不需要返回类型
c.不需要返回值
注意:构造对象时,系统默认无参构造,相当于初始化。如果自定义了构造函数,则不会默认无参构造也不会自动创建无参构造方法。同时,一个类中也可以有多个构造函数,它们名称相同但参数列表不同,这称之为构造方法的重载。
5)构造方法和一般方法的区别
a.两个方法的定义格式不同。
b.构造方法是在对象创建时被调用的。初试化操作只执行一次。而一般方法是对象创建之后被调用,可以被调用多次。
6)构造代码块和构造方法的区别
构造代码块:对所有对象进行初始化。
构造方法:对与之对应的对象进行初始化。
7)创建一个对象都在内存中做了什么事情?
a.先将硬盘上指定位置的.class文件加载进内存。
b.执行文件中的main方法,在栈内存中开辟变量存储空间。
c.在堆内存中开辟实体空间,分配内存首地址值。
d.对实体空间中进行属性的空间分配,并进行默认初始化。
e.对空间中的属性进行显示,对实体中的构造方法进行初始化。
f.main方法的栈区分配变量
g.调用实体对应的构造函数,将首地址值赋值给变量,p指向了该实体对象。
8)封装
封装是指隐藏对象的属性和实现细节,仅仅对外提供访问。
实现步骤:
a.修改属性的可见性:设为private。
b.创建getter和setter方法:用于属性的读写。
c.在getter和setter方法中加入属性控制语句:对属性值的合法性进行判断。
9)this关键字
this.为特殊引用,引用当前对象或类方法的地址。
this语句必须输首行代码。
this:代表对象,是所在方法对对象的调用。
在定义方法时,如果该方法内部使用到了调用该方法的对象,就用this来代表这个对象。
调用格式:this(实际参数)
this对象后面跟着.调用的成员属性和成员方法/一般方法。
this()调用的是本类中的无参构造方法。
注意:
a.静态方法只能访问静态成员,不能访问非静态成员。
因为静态成员加载时,优先于对象存在,所以没有办法访问对象中的成员。
b.静态方法中不能使用this,也不能随便super关键字,因为this代表对象,而super代表父类,如果父类不是静态类是不能调用的。
c.main函数是必须是静态的。
10)静态成员的定义
a.成员变量:如果该成员变量的数据在所有对象那里都是一样的话,可以静态化。如果不是一样的,说明是某对象特有的,要存储在对象中而不是类中。被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说它不依赖于类的实例,被类的所有实例共享。只要这个类被加载,JVM能根据类名在运行时的方法区中找到它们。因此,static对象可以在它任何对象创建之前访问,无需引用任何对象。静态成员变量可以直接通过类名调用,也可以用对象来调用。
b.成员方法:该方法内是否访问了对象中的特有数据,如果有访问特有数据,那么不能被修饰成静态的。静态成员方法可以直接通过类名调用,也可以用对象来调用。注意:静态成员方法中不能使用this和super关键字,因为实例对象的属性和方法需要在创建对象之后才会存在,而静态成员方法属于类。因为静态成员方法只能访问静态成员,所以不能用super来调用父类(父类并不完全是静态的)。
11)成员变量和静态变量的区别:
a.成员变量属于对象,静态变量属于类。
b.成员变量存在于堆方法中,静态变量存在于方法区中。
c.成员变量随着对象创建而存在,随着对象死亡而消失;静态变量随着类的创建而存在,随着类的消失而消失。
d.成员变量只能被对象调用,静态变量可以被类名、所有对象调用。
e.所有成员变量被称为对象特有数据,而静态变量成为对象共享数据。
12)静态代码块
静态代码块:就是一个有静态关键字标识的一个代码块区域,定义在类中。
作用:可以完成类的初始化,静态代码块随着类的加载而执行,而且只执行一次。如果和main方法在同一类中,优先执行静态代码块。
执行顺序:
先执行父类的静态代码块,然后执行子类的静态代码块;然后执行父类的非静态代码块,再执行父类的构造方法;之后再执行子类的非静态代码块,再执行子类的构造方法。
(优先级)静态代码块>非静态代码块>构造方法>普通方法
13)main方法
public static void main(String[] args){
}//main方法的声明
注意:如果有public关键字修饰,则文件名必须和类名一致。一个Java源文件里只能包含一个public类定义。
主方法的参数是一个字符串数组类型的数组,在运行时,可以在控制台指定具体的参数进行传递。JVM会自动将这些字符串参数作为args数组中的元素,进行存储。
静态方法(如main方法)如果要调用非静态方法时,需要new一个对象,对象可以调用非静态成员。因为静态成员属于类,而非静态成员属于对象。静态方法中对象的非静态调用与本类无关。
14)继承
继承让类与类之间产生了联系。
继承方式:
public class Student(子类) extends Person(父类)
注意:private私有类是无法继承的。
a.父类:其是由多个类不断向上抽取共性内容得来的。
如麻雀类、鸽子类、鹰类的父类是鸟类。
注意:Java中对于继承只支持单继承。
单继承:一个类只能有一个父类。(但能实现多个接口)
多继承:一个类可以有多个父类。(接口是多继承的)
b.继承出现之后,类中成员的特点:
注意:继承不能继承私有成员。调用方法时会优先调用子类的方法。
15)Object类:如果一个类不继承其他类,则默认继承Object类。
Object类方法:
toString():获得字符串表示。可以在子类中重写toString方法。
equals():当前对象与参与对象比较是否相等,相等返回true。
equals(b):默认比较内存地址。
注意:Object中比较内存地址,基本类型默认比较内容值。
16)instanceof关键字:运行期类型识别,判断其父类,如果是则返回true。
格式:
if(s instanceof Student)//判断对象s是否属于Student类
17)成员变量:当子父类中出现一样的属性时,子类类型的对象调用该属性,只是子类的属性值。如果想调用父类中的属性值,需要关键字super。
this:代表是本类类型的对象。
super:代表父类。
注意:子父类中通常不会串行同名的成员变量,因为父类中定义了,子类中就不用重复定义了,直接继承。super不能在静态方法中使用。
18)成员方法:当子父类汇总出现一样的方法时,建立子类对象会调用子类的方法。这被称为方法的重写。
多态:多态的前提是继承,在使用多态调用方法的时候,编译器检查父类中是否有该方法,如果有才能编译通过。指子类对象可以直接赋予父类变量(引用多态:父类的引用指向本类的对象),但是运行时依然表现出子类的行为特征,这意味着同一个类型的对象在执行同一个方法时可能有不同的结果,因为有重写的发生。
如果子类中需要调用父类的方法,则在子类方法中使用surper.父类方法名(参数列表)的格式来调用。
注意:重写和重载的区别:
a.重载是在一个类中,函数名相同但参数列表或参数值数据类型不同。
b.重载方法可以有不同的返回值类型,也可以有不同的访问修饰符。
c.重写覆盖是在子类中,方法的声明和返回值完全相同,但实现不同。在Java规定中,子类方法的访问权限不能比父类中被重写的方法的访问权更小,否则重写失败。
d.构造方法不能被继承所以可以被重载却无法被子类重写,但可以通过super()来调用父类的构造方法。
19)类型的转换:
向上转型(自动转换):子类的实例对象转成父类类型,用父类类型的引用变量来引用子类实例对象,向上转型后只能调用父类中定义的通用成员,子类特有的成员被隐藏。如果出现重写,则调用重写后的方法。
实例:
向下转换(强制转换):大类型转换成小类型
20)构造方法:构造方法必须和类同名,并且没有返回值。子类构造函数运行时,会先运行父类的无参构造方法。因为子类所有构造函数的第一行,都有一条隐藏语句:super()。
super():表示父类的无参构造。因为子类继承父类,会继承父类中的数据,所以要先初始化父类的数据。子类在进行对象初始化(调用构造方法)时,先必须调用父类的构造函数。
注意:如果父类中没有无参构造,那么子类的构造函数内必须通过super(实参)来指定要访问父类的构造函数。如果子类构造函数中用this来指定调用自己的构造函数,这时同时会访问父类的构造函数。
当构造方法中同时super()和this()时,super()放在第一行。
21)构造方法的细节:
a.什么时候使用继承?当类与类之间存在着所属关系时,具备了继承的前提。但不要仅仅因为想获取其他类中的已有成员使用继承。如果继承后,被继承的类中的功能都可以被其子类所具备,则继承成立。
b.当子类重写方法覆盖父类时,子类方法的权限必须要大于父类。其次,覆盖时静态只能覆盖静态,非静态只能覆盖非静态。
修饰符权限:
默认为default。
类和接口不能私有。
22)继承的扩展:
当创建了一个导出类的一个对象时,这个子对象和你直接用基类创建的对象是一样的。那是不是真的会在导出类的内部再new一个父类的对象?不会的,实际上只是调用父类的构造器。
23)final关键字
a.final是一种修饰符,可以用来修饰类、方法、变量。
b.被final修饰的类是一个最终类,不可以再被继承。
c.被final修饰的方法是一个最终方法,不能重写覆盖。但可以重载。
d.被final修饰的变量是一个常量,只能赋值一次。
注意:引用类型因为保存的是地址,所以被final修饰后内容可变。
final Point a=new Point();
a.x=30;//是可以对实例对象进行改动的。
而且父类的private修饰的成员方法是不能被子类覆盖的,所以默认是final类型的。
24)抽象类abstract
Public abstract class 类名{
}
有的方法和类在进行共性抽取时,得出的结果并不具体,是抽象的。需要被关键字abstract修饰,声明为抽象方法或抽象类。
抽象方法一定要在抽象类中。
a.抽象类的特点:
①抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(不能描述变量)
②抽象方法只定义方法声明,并不定义方法实现。
③抽象类不可以创建对象(实例化),只能定义引用变量。
④只有通过子类继承抽象类并重写覆盖了抽象类中所有的抽象方法后才能创建对象,否则子类也是抽象类。
b.抽象类的细节:
①抽象类中是否有构造方法?有无参构造供子类对象初始化使用。
②抽象类中是否可以声明非抽象方法?可以,抽象类的一般方法的定义,是需要定义属性和行为的。只不过,抽象类比其他类多了抽象方法,少了能创建对象这一部分。
注意:抽象类中可以使用final、private、static等关键字。抽象类和抽象方法目的仅仅是为了不让该类创建实例对象。
异常Throwable和Exception
1)异常的类图
Object类是所有类的基类。
Throwable类是Error类和Exception类的父类,用来定义所有可以作为异常被抛出的类。
2)Error类和Exception类的区分
Error是编译时错误和系统错误,系统错误一般不会出现,编译错误会被编译器提示。如内存溢出、线程死亡错误。
Exception又分为RunTimeException和其他Exception。RunTimeException是运行时异常,又称为不受检查异常,所以在编译时不会提示有这类异常,但是运行时会报错,如经典的分母为0、空指针,数组索引越界异常、类型转换异常等。其他Exception是受检查异常。必须通过解决才能编译通过,可以通过throw到上层,或者用try-catch语句处理。
3)finally代码块用于释放被占用的相关资源。
4)捕获异常
如果抛出异常,并且其中catch中有return语句,这个return语句会先执行,执行之后将结果保存在缓存中,再去查看是否有finally,如果有finally,会先执行finally中的语句,如果finally中有return,那么会覆盖掉之前缓存中的返回值,只返回finally中的返回值。
5)throw手动抛出异常:执行异常的抛出动作类似return语句,当程序出现逻辑错误,不自动创建并抛出异常,可以手动判断逻辑错误,手动创建异常对象并抛出。底层的异常往上层抛。
if(存在异常) {
Exception e=new Exception();//构造异常对象
throw e;//抛出异常
}
注意:在方法将发生的异常抛出:throws
[修饰符][返回类型][方法名](参数列表)[throws(异常类)]{
}
6)异常包装
捕获的异常对象,包装成其他类型再做抛出。把不能抛出的异常包装成能抛出的异常再抛。
7)assert关键字
assert表示“断言”,有两种使用方法:
a.assert 表达式;//若表达式为true,则程序继续执行。若表达式为false,则抛出一个AssertionError异常来中断程序。
b.assert 表达式:错误信息;//抛出的异常中带有错误信息
注意:使用assert时,不能在表达式中完成任何程序实际所需的行为(只能做判断)。
8)重写异常:如果父类中被重写的方法抛出了异常,则子类中的方法也要抛出异常。但是子类不能抛出比父类更多的异常。如:父类方法抛出Exception,子类只能抛出IOException或者比Exception小的异常。
包裹类型
对于基本数据类型,Java提供了对应的包裹(wrap)类型。这些包裹类型将一个基本数据类型的数据转换成对象的形式,从而可以使它们可以像对象一样可以参与运算和传递。
自动装箱和自动拆箱:
1)基本类型值:自动包装成包装对象:
Integer a=6;//编译器编译成:Integer a=Integer.valueOf(6);
2)自动拆箱:
int i=a;//编译器编译成:int i=a.intValue();
接口
接口是极端的抽象类。不关心具体实现细节,只规定必须的方法。
implements代替extends来实现接口。
interface代替class。
1)接口的定义
a.接口值定义常量。
b.接口只定义抽象方法。
c.接口只能继承接口。
d.接口没有构造方法。
注意:在接口中定义常量是,默认为final static。
在接口中定义抽象方法时默认abstract。
类可以实现多个接口:class A extend B implements X,Y,Z{}
集合
集合是用来存放一组数据的数据结构。
数组的缺点:长度固定;访问方式单一只能下标访问,增删改查操作繁琐。
集合的继承结构:
1)Collection是对象集合
Collection有两个子接口List(),Set();
List()可以通过下标来取得值,值可以重复。Set()只能通过游标来取值,并且值是不能重复的。
List()的实现类:
ArrayList是线程不完全的,Vector是线程安全的。都由数组实现。
LinkedList是线程不安全的,由链表实现。
2)Map()是键值对集合
Map()的实现类:
HashTable是线程安全的,不能存储null值。
HashMap是线程不安全的,可以存储null值。
3)重点
a.ArrayList数组列表类:封装了一个数组及其操作代码和各种方法,内部数组默认初始容量放满后会自动扩容。
方法:
ArrayList.add(数据);//添加数据
ArrayList.get(int i);//访问指定下标数据
ArrayList.remove(int i);//移除指定位置数据,返回被移除的数据
ArrayList.remove(数据);//找到第一个相等的数据,找到移除后返回true。
ArrayList.size();//返回元素的数量
b.LinkedList双向链表类:
方法:
和ArrayList有相同的方法,但LinkedList效率更高。
LinkedList两端数据操作方法:
LinkedList.addFirst(数据);
LinkedList.addLast(数据);
LinkedList.getFirst();
LinkedList.getLast();
LinkedList.removeFirst();
LinkedList.removeLast();
c.HashMap哈希表、散列表:
存放键值对,用键来快速定位数据,来提取键对应的值。
特点:键不能重复,是无序的。
HashMap中的key-value都是存储在数组entry[]中的
HashMap的实现不是同步的,意味着线程不安全。
方法:
put(key,value);//放入键值对数据,重复的键会覆盖旧值。
get(key);//获得键对应的值,键不存在返回null
remove(key);//移除键对应的数据,返回被移除的值
size();//键值对的数量
哈希运算过程:
HashMap内部,使用entry[]数组存放数据,Key.hashCode()获得键的哈希(地址)。
根据哈希值和数组长度产生下标值。
新建entry对象来封装键值对和下标值。
把entry对象放入下标值位置:
如果是空位置直接放入。
如果有数据,用equals()比较是否相等,找到相等的话就覆
盖旧值,如果没有相等的就用链表连接在一起。
d.hashCode():
其是Object的方法,默认实现用内存地址作为哈希值。
e.ArrayList和LinkedList的区别和联系:
①ArrayList内部是数组结构,LinkedList内部是链表结构。ArrayList()的优点是查询效率比较高,而LinkedList()链表的优点是添加和删除效率高。
②ArrayList数组结构每个元素都有下标索引,那么当向其中添加一个数据的话,则每个元素的索引都会发生变化。这样效率低下,但是通过索引很容易找到具体的元素。
LinkedList是链表结构,当向其中添加或删除一个数据的时候,只需要对其前后的数据改变引用(指针)即可,但是由于链表必须从头到尾进行遍历,查询比较耗时。
f.List()的遍历实现:
①for循环
②for each循环
③Iterartor迭代器
实现:
g.泛型
规定装入集合的数据类型,格式<数据类型>,增强程序的可读性和稳定性。
注意:泛型不能用在静态属性上。泛型只能指定包装类型,不能是基本类型。
h.在只遍历key的时候使用keySet(),在只遍历value的时候使用values(),在遍历key-value的时候使用entrySet();
文件、字符、字节操作流 (IO)
字节流、字符流四个主要接口:InputStream、OutputStream、Writer和Reader。前两个是字节流,后两个位字符流。
1)File
封装一个磁盘路径字符串,提供了一组对文件、文件夹的操作方法,可以封装文件夹路径、文件路径、不存在的路径。
方法:
getName();//获取文件名
getPatrent();//获取父目录(文件夹)
getAbsolutePath();//完整路径
length();//文件字节量,对文件夹无效
isFile();//判断是否是文件
isDirectory();//判断是否是目录(文件夹)
createNewFile();//新建文件,文件已存在不会新建,返回false;目录不存在会报错。
mkdirs();//创建多个文件夹
delete();//删除文件、空目录(文件夹)
list();//得到所有文件名
listFiles();//得到所有文件封装的File对象
2)Stream
数据的读写操作,抽象成数据在管道中流动。
单方向流动
输入流:中能用来读取数据(从管道中流入内存)
输出流:只能用来输出数据(从内存中流到管道)
只能从头到尾顺序流动一次,不能反复。如果要重复流动,要创建新的流。
a.InputStream,OutputStream字节流
方法:
write(int b);//只输出int类型(占四个字节的数据)末尾的一个字节值
write(byte[],start,length);//输出byte[]数组中从start开始的length个字节值。
read();//读取一个字节值,因为默认int型,所以其他三个字节都为0,读取结束后,再继续读取会返回-1。
read(byte[] buff);//按数组的长度,读取一批字节值,存放到指定的数组中,并返回这一批的字节数量,读取结束后,再读取会返回-1;
b.FileInputStream,FileInputStream文件流
ObjectInputStream,ObjectInputStream——对象序列化、反序列化
序列化:把一个对象的信息,按固定的字节格式,变成一串字节序列输出。
方法:
writeObject(Object obj);//把对象变成一串字节序列输出。
readObject();//读取序列化数据
Serializable接口——被序列化的对象,必须实现此接口
不序列化的对象:
static:属于类,不随对象被序列化输出。
3)Reader,Writer字符流
字符流就是在字节流的基础上,加上编码,形成的数据流。
字符输入流:Reader
常用方法:
read();
read(char[]);
read(char[],offset,len);
字符输出流:Wirter
常用方法:
writer();
writer(char[]);
writer(char[],offset,len);
Writer(string);
flush();//刷新缓冲区
注意:close()方法默认调用了flush(),但flush()方法只刷新缓冲区,而close()关闭IO流
4)字符流与字节流的区别:字符流虽然以字节流为基础,但是字节流可以支持声音、视频、图片、文本等所有文件类型,而字符流只支持文本文件。
5)缓冲的字符流
带缓冲区的字符输入流:BufferedReader
常用方法:readLine();//读取一行,如果为文件末尾,返回null;
带缓冲区的字符输出流:BufferedReader
常用方法:writer(String s);//将字符串写入输出流。newLine()根据系统的行分隔符进行换行。
6)转换流
InputStreamReader将字节流转换成字符流 输入
InputStreamWriter将字节流转换成字符流 输出
7)字节数组流
ByteArrayOutputStream字节数组输出流
特点:可以将数据写入byte数组中,并且该缓冲区可以随写入数据而自增。
常用方法:
write(byte[]);//将byte[]中的值写入缓冲区字节数组,该缓冲区随着数据的增多而增多。
toString;//要想获取缓冲区的字节数据,可以通过该方法将其转换为字符串。
8)synchronized关键字
synchronized关键字用于保证线程安全。由这个关键字修饰的方法或者代码块保证了同一时刻只有一个线程执行该代码。
8)注意事项
字符流写入要刷新/关流才能写入,字节流不需要刷新,因为字节流是一个字节一个字节读和写的。
字符编码
String s="中国我爱你";
byte[] b=s.getBytes();//转换为系统默认编码
byte[] b1=s.getBytes("UTF-8");//转换为指定编码
String s1=new String(b1,"UTF-8");//从指定类型转换成Unicode
OutputStreamWriter和InputStreamReader字符编码流:
OutputStreamWriter把Java的Unicode编码字符转换成其他编码输出,InputStreamReader读取其他编码字符,转换成Unicode字符。