本文仅是自己的学习笔记,更多学习内容请看b站尚硅谷康老师《Java基础》的讲解
Java介绍
学习Java的三条主线
- 类的组成:属性、方法、构造器、内部类、代码块
- 性质:封装性、继承性、多态性、【抽象性】
- 关键字的使用
Java技术体系平台
- Java SE(标准版):支持面向桌面级应用的Java平台,定位个人计算机的应用开发
- Java EE(企业版):定位在服务器端的Web应用开发
- Java ME(小型版):运行在移动终端的平台
Java开发工具
- JDK:Java程序开发工具包,包含JRE与开发人员工具包
- JRE:Java程序的运行开发环境,包含JVM(Java虚拟机)与核心类库
小结
JDK=JRE+开发工具集
JRE=JVM(用来看编译好的class文件,并执行字节码文件)+Java SE标准类库
其中: JVM是Java虚拟机,用来执行java程序的,内嵌在不同的操作系统中,因此是java语言有了更好的跨平台性。
Java基本语法
java语言编写需要了解的基本情况
最基本的表达形式:
Class HelloChina{
-------public static void main(String[] args){
-------System.out.println(“”);
}
注意:一个源文件中可以声明多个类,但是最多只能有一个类使用public进行声明。且要求声明为public的类的类名与源文件名相同
注释:
-
单行注释://注释文字
-
多行注释:
/*
注释文字1
注释文字2
注释文字3
*/ -
文档注释(**文档注释内容可以
-
List item
被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档**)操作方式:javadoc -d 自定义文件名 -author -version HelloWorld.java
/**
@author
@version
*/
变量与运算符
标识符
规则
- 由英文字母、0-9、_或$组成
- 数字不可以开头
- 不能包含空格
- java严格区分大小写
规范(多单词组合下的情况)
- 包名:所有字母都小写
- 类名、接口名:所有单词首字母大写
- 变量名、方法名:第一个单词字母小写,其他字母单词大写
- 常量名:所有字母都大写
变量
整型:byte(1字节=8bit)\short(2字节)\int(四字节)\long(8字节)
注意:声明long类型变量时,需要提供后缀,后缀为‘l’或者‘L’ 如:long l1 = 12313232L
浮点类型:double/float
注意:声明float类型变量时,需要提供后缀,后缀为‘f’或者‘F’
浮点类型:char(2字节)
注意:char c = ’ ‘; ‘ ’内有且只能只能放一个字符; ‘ ’内直接用Unicode的值来表示字符常量:‘\uXXXX’;‘ ’内直接使用转义字符;变量c可以直接赋值一个整型用来表示ASCII码
布尔类型:boolean
只有ture与false两个值
字符串类型:String,字符串能够相加减并赋值
基本数据类型变量间的运算规则:
- 自动类型提升
规则:当容量小的变量与容量大的变量做运算时,结果自动转换为容量大的数据类型(容量大小指的是数据的范围大小)
如:int i1;long l1=i1;
总结:short—>int能赋值给long—>float—>double(反之不行) - 强制类型转换
如:long g1;int i1 =(int)g1;
不同进制的标识:
- 十进制:0-9
- 二进制:0-1(以“0b”或者“0B”开头表示)
- 八进制:0-7(以“0”开头表示)
- 十六进制:0-9、a-f(以“0x”或“0X”开头表示,此处的a-f不区分大小写)
二进制如何表示整数:
-
计算机数据的存储使用二进制补码形式存储,并且最高位是符号位。
(1)、正数:最高位是0
(2)、负数:最高位是1 -
规定
(1)、正数的补码与反码。原码一样,称为三码合一
(2)、负数额度补码与原码和反码不一样:
负数的原码是把十进制转化为二进制,然后最高位设置为1;负数的反码是在原码的基础上,最高位不变,其余位取反;负数的补码:反码+1 -
二进制转换成八进制:每三位看成一位
-
二进制转换成十六进制:每四位看成一位
运算符
算术运算符:除:/、取模(取余):%、字符串连接:+
赋值运算符:赋值:“=”、+=、*=、%=、/=:如m+=5—>m=m+5(该过程属于强制类型转换)
比较运算符:instanceof:检查是否是类的对象,如“Hello instanceof String=ture”;”==“:对于基本数据类型判断值是否相等,对于引用数据类型比较地址值是否相等
逻辑运算符:&或&&:且(当左边是ture的时候&和&&都会执行右边的,但当左边是false的时候,&会执行右边的,但&&不会执行右边的)、|或||:或当左边是false的时候|和||都会执行右边的,但当左边是ture的时候,|会执行右边的,但||不会执行右边的)、!:非、^:异或(两边相同则为false,两边不同则为ture)
位运算符(都是针对十进制数的二进制):<<:左移(十进制数的二进制向左移动一位且向后补0,相当于在原有的基础上乘以二【正负数在一定范围内都适用】)、>>:右移(相当于在原有的基础上除以二,除不尽就向下取整【正负数在一定范围内都适用】)、>>>:无符号右移(与>>不同的是负数向右移动的时候“>>>”是补0,而“>>”是补1)、&:与运算、|:或运算、^:异或运算、~:取反运算
条件运算符:(条件表达式)?表达式1:表达式2---->条件表达式为ture,则执行表达式1,否则执行表达式2(开发中凡是可以适应条件运算符位置的地方,都可以改写成if-else,反之能使用if-else结构的,不一定能改写成条件运算符,但条件运算符的运算效率更高)
Lambda运算符:略
随机数:Math.random();代表的是[0.0 , 0.1]的double类型随机数
根号:Math.sqrt();
流程控制
分支结构(if-else、switch-case)
基本的Scanner输入:
- 导入import java.util.Scanner
- 创建一个Scanner类的实例
- 调用Scanner类中的方法,获取指定类型的变量
- 关闭资源,调用Scanner类的close()
- 使用的基本模板:
Scanner scan = new Scanner(System.in);String name = scan.next()==(还有nextInt、nextDouble等nextXXX方法,要输入char类型的使用next().charAt(0););
switch-case选择结构:switch(表达式){ case 常量1://执行语句//break……default//break}
循环结构(for、while、do-while、foreach)
bresk和continue的区别:break是结束当前最近的循环结构;continue是结束当次最近的循环结构;break和continue可以配合label使用,用来结束指定的循环,==如:label:for(;;;){ break label;}
数组
数组的分类:
- 按照元素的类型分为基本类型元素的数组和引用数据类型(类、数组、接口、枚举、注解、记录)类型元素的数组
- 按照数组的维数来分
一维数组的初始化:
- (1)、声明:数据类型[ ] 数组名;初始化:数组名 = new 数据类型[ ]{数据元素、……}如:
int [ ] arr;arr = new int{};
- (2)、声明一样;初始化:数组名 = new 数据类型[数组长度]如:
int [ ] arr;arr = new int[4]
- (3)、数据类型 数组名[ ] = new 数据类型[数组长度]或者数据类型[ ] 数组名 = {数据元素、……}如:
int arr[] = new int[4];int[ ] arr = {}
- (4)、数组的长度:数组名.length
二维数组的初始化:
- 静态:
int [ ][ ] arrs = new int[ ][ ]{{}、{}……}
- 动态:
int [ ][ ] arrs = new int[长度][长度或者省略 ]
- 长度:
arrs.length
就是二维数组最外层的长度,arrs[0].length
输出最内层的长度
Arrays工具类(java中针对数组的一些方法):
- 判断数组是否相等
equals:Arrays.equals(arr1,arr2 )
- 输出数组所有信息
toString:Arrays.toString(arr1);
- 将指定值填充到数组中fill:
Arrays.fill(arr1,int类型);
- 排序sort:
Arrays.sort(arr1);
- 二分查找binarySearch:
Arrays.(arr1,int key);
JVM运行时内存环境
内存分为五个部分:程序计数器、虚拟机栈、本地方法栈、堆、方法区
与目前数组相关的内存结构:虚拟机栈(用于存放方法中声明的变量,即变量名与地址)、堆(用于存放数组的实体,即数组中所有的元素)
Java面向对象(Object)编程
面向对象编程(基础)
类的声明与使用
-
设计类,其实就是设计类的成员
class Person{
} -
类的内部成员
成员一:属性、成员变量、field(字段、域)
成员二:(成员)方法、函数、method
public class Person{
String name;//属性
public void fist{//方法
System.out.println(“能打人”)
}
} -
类的调用
public class PersonTest{
public static void main(String[ ] args){
Person p1 = new Person();
p1.name = Hua;
p1.fist();
}
}
对象在内存中分配涉及到的内存结构(理论)
- 虚拟机栈(stack):方法(比如main)内定义的变量(p1)和方法,(p1的地址)存储在栈中
- 堆(heap):new 出来的结构(比如:对象的实体),包括对象中的属性(暂放个空内存)
- 方法区(method area):存放类的模板,比如:Person类的模板
成员变量与局部变量
引论:按照变量在类中声明的位置的不同:成员变量(或属性)、局部变量(方法内、方法形参、构造器内、构造器形参、代码块内等)
不同点:
- 类中声明的位置不同:属性:声明在类内,方法外的变量;局部变量:声明方法、构造器内部的变量
- 在内存中分配的位置不同:属性:随着对象的创建,存储在堆空间中;局部变量就是存储在栈空间
- 生命周期:属性:跟随对象的创建于消亡;局部变量:跟随方法在栈的消亡
- 是否可以有权限修饰符进行修饰:属性可以而局部变量不可以
- 是否有默认值:属性:都有默认初始化值;局部变量:都没有默认初始化值
方法
方法的声明模板:权限修饰符【其他修饰符】 返回值类型 方法名(形参列表)【throws 异常类型】{//方法体}
注意点:
- Java里的方法不能独立存在,所有的方法必须定义在类里。
- Java中的方法不调用不执行,每调用一次,就执行一次
- 方法内可以调用本类中的其他方法或属性
- 方法内不能定义方法
- return照常使用
对象数组
含义:数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象数组(Student[ ] students = new Student[2]这;只是创建了Students类型的数组,但Student数组中每个student[i]对象并没有创建,需要循环student[i] = new Studen()
)
重载(overload)
定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,满足这样特征的多个方法,彼此之间构成方法的重载
总结:”两同两不同“
- 两同:同一个类,相同的方法名
- 两不同:参数列表不同。(1)参数的个数不同(2)参数的类型不同
- 注意:方法的重载与形参的名、权限修饰符、返回值类型都没有关系。
可变个数形参的方法:在调用方法的时候,可能会出现方法形参的类型是确定的,的那参数的个数不确定。此时,我们就可以使用可变个数形参的方法。格式如:public void A(int ... 参数名);
- 注意:(1)、可变个数形参的方法在调用的时候,针对于可变的形参赋值的实参的个数可以为0个、1个或者多个(2)、可变个数形参的方法与同一个类中,同名的多个方法之间可以构成重载;特例:与public void A(int [ ] 参数名)是同一个方法不构成重载)(3)、 可变个数的形参必须声明在形参列表的最后,并且只能在形参列表中出现一次
方法的值传递机制
- 方法内局部变量的值传递机制:(1)、基本数据类型的局部变量:传递的是数据值,比如:
int n,m;m=10;n=m;n=9;操作过后m的值不会变还是10;
(2)、引用数据类型的局部变量:传递的是地址值,比如:int [ ] a = {1,2,3,4}、int [ ] b = a;b[0] = 10;
此时a[0] = 10
,对象类型也一样`
- 方法中参数的传递机制:(1)对于基本数据:
m=10;test(m)【test函数是将形参加一】
;此时m的值还是为10
(2) 对于引用数据类型:A a = new A(),a.age = 10;test(a)【test函数是将对象中的属性加一;此时a.age = 11
结论:Java中的参数传递机制是值传递(包括数据值传递、地址值传递)
package与import
package:包,用于指明该文件中定义的类、接口等机构所在的包 ;设置包的作用:
- 包可以包含类与子包,划分项目层次,便于管理
- 帮助管理大型软件系统;将功能相近的类划分到同一个包中。
- 解决类名冲突的问题
- 控制访问权限
- 同一个包下多个源文件中的不同类可以随便调用
import:import 包名.类名
封装性
含义:所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只指向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息,相当于我们只需要知道类的使用方法即可,不需要知道具体如何实现的;其设计思想就是把该隐藏的隐藏起来,该暴露的暴露出来
权限修饰符:private、缺省、protected、public,用修饰词修饰的属性或者方法在各个地方是否能被调用(通过类来调用)的情况如下,要想使用被限制的属性可以使用自定义的set(在类的内部定义在外部类使用)和get(在类的内部定义在外部类使用)方法、构造器来调用赋值 ,类的修饰词只有public和缺省
构造器(constructor)
含义:Person p1 = new Person();
作用:(1)、搭配new关键字,创建类的对象;(2)、在创建对象的同时,可以给对象相关属性赋值
(3)、创建指定属性值的对象,造一个轮胎23村的车子
说明:
- 权限修饰符 类名(形参列表){ }
- 创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限默认与定义的类相同
- 一旦类中显示声明了构造器,则系统不在提供默认的空参的构造器
- 一个类中可以声明多个构造器,彼此之间构成重载
实例变量(或属性)的赋值过程
- 在类的属性中,可以有哪些位置给属性赋值?:(1)默认赋值、(2)显示赋值、(3)构造器赋值、(4)通过”对象.方法“赋值(5)、通过”对象.属性“赋值
JavaBean(一个具有某些特征的java类)
理解:JavaBean是一种Java语言写成的可重用的组件,相当于做了一个扳手,这个扳手会在很多地方拿去被用。
特征:(1)、类是公共的(2)、有一个无参的公共的构造器(3)、有属性,且有对应的get、set方法
面向对象编程(进阶)
this
一个类中的多个方法可以相互调用,调用过程默认省略this
- 利用this解决最通用的问题:当我们在声明一个属性对应的setXxx方法、其他方法、构造器时,通过形参给对应的属性赋值。如果形参名和属性名同名了,那么可以通过this来区分这两个变量,比如其中使用this修饰的是属性,没有用this修饰的是形参,在构造器中也可以使用,this代表的含义就是当前创建的对象(指目前new出来的对象本身,写在对象所属于的类中),可以在一个类中直接写this代表当前创建的对象,可单独作为一个参数
- this可以调用的结构:成员变量、方法、构造器
- this调用构造器:(1)、格式:this(形参列表)(2)我们可以在类的构造器中,调用当前类中指定的其他构造器、(3)、this(形参列表)必须声明在当前构造器的首行,举例如下图(4)、this(形参列表)在构造器中最多声明一个(5)、如果一个类中声明n个构造器,则最多有n-1个构造器可以声明有this(形参列表)的结构
![在这里插入图片描述](https://img-blog.csdnimg.cn/5921d5b427d4425d9fde2485a2602315.png#pic_center
public User(){
//模拟对象创建时,需要初始化50行代码。
}
public User(){
this();
this.name = name;
}
public User(){
this(name);
//this.name = name;
this.age = age;
继承性
子类可以有独立于父类的其他方法
理解:(1)、自下而上:定义了一个类A,在定义另一个类B时,发现类B的功能与类A相似,考虑类B继承类A。(2)、定义了类B、C、D等,发现B、C、D有类似的属性或方法,则可以考虑将相同的属性和方法进行抽取,封装到类A中,让B、C、D、继承于类A,同时B、C、D中的相似的功能就可以删除了
优点:减少了代码冗余,提高了代码的复用性;有利于功能的扩充;继承的出现让类之间产生了‘is-a’的关系,为多态的使用提供了前提;继承描述事物之间所属的关系,这种关系是‘is-a’的关系,可见,父类更通用,更一般,子类更加具体。
格式:class A{ };class B extends A{ };
基本的概念:类A:父类、superclass、超类、基类;类B:字类、subclass、派生类
注意:子类就获取到了父类中声明的所有属性与方法,但是由于封装性的影响,可能子类不能直接调用父类中声明的属性或方法,同样可以通过get于set方法进行访问;支持多层继承、单继承不支持多重继承(不能有多个父亲)
默认的父类:Java中声明的类、如果没有显式的声明其父类时,则默认继承于java.lang.Object
方法的重写(overwrite/override)
原因:子类在继承父类以后,就获取了父类中声明的所有方法。但是,父类中的方法可能不太适用于子类,换句话说,子类需要对父类继承过来的方法进行覆盖、覆写的操作。
规则:子类如何准确地对父类重载的方法进行重写:
- 父类被重写的方法于子类重写的方法的方法名和形参列表必须一致
- 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
- 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型必须是void
- 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须与被重写的返回值类型相同。
- 父类被重写的方法的返回值类型是引用数据类型(比如类),则子类重写的方法的返回值类型必须与被重写的返回值类型相同 或 是被重写的返回值类型的子类。
- 可省:子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同 或 是父类被重写的方法抛出异常类型的子类
super(父类的)
使用super的情况:(1)、子类继承父类以后,对父类的方法进行了重写,在子类中,可以利用super实现对父类被重写的方法进行调用;(2)、子类继承父类以后,发现子类和父类中定义了同名的属性,可以利用super进行区分。
super可以调用的结构:属性、方法、构造器
- 属性和方法:子类继承父类以后,我们就可以在子类的方法或构造器中,调用父类中声明的属性或方法(在满足封装性的条件下),需要使用"super."的结构,表示调用父类的属性或方法;一般情况下,我们可以考虑省略”super."的结构,**但是,如果出现子类重写了父类的方法或者出现了同名的属性,则必须使用“super."的声明,用来表示调用父类被重写的方法或父类声明的同名的属性 **
- 构造器:
——子类继承父类时,不会继承父类的构造器。只能通过”super(形参列表)“的方式调用父类指定的构造器
——规定:”super(形参列表)“,必须声明在构造器的首行
——在构造器的首行,this和super只能选择其中之一
——如果子类构造器中既没有this也没有super,则该子类默认使用super()调用父类的空参构造器;
——我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或者间接的调用到父类的构造器。 【子类实例化对象的过程】
多态性 【只针对重写的方法,不针对属性】
生活举例:女朋友想养宠物,我给了她一条萨摩耶(子类狗的对象萨摩耶赋给了父类宠物对象);孩子想要一个玩具,我给他一个塑胶奥特曼(子类塑胶玩具的对象奥特曼赋给了父类玩具对象)
Java中多态性的体现:指的就是子类对象的多态性,父类引用指向子类的对象。(或者子类的对象赋给父类的引用。如:Pets a = new Dog();a.eat();
其中a.eat()运行的是子类Dog的方法【编译看左边,运行看右边】
多态性的重写:(1)、要有类的继承关系(2)、要有方法的重写
举例:
public class AnimalTest{
public static void main(String[ ] args){
AnimalTest test = new AnimalTest();
//子类多态性的优点……………………………………………………………………
test.adopt(new Dog());
test.adopt(new Cat());
}
public void adopt(Animal animal){
animal.eat();
}//要想调用不同子类对象的方法,只需要利用子类的多态性将子类对象赋值给父类引用,即Animal animal = new Dog(),不需要在为猫或狗在定义方法了
//子类多态性的优点……………………………………………………………………
}
class Animal{
pubic void eat(){}
}
class Dog extends Animal{
public void eat(){}
public void watchDoor(){}
}
class Cat extends Animal{
public void eat(){}
public void play(){}
}
优点及弊端:
- 优点:如上面代码,使用父类做方法的形参
public void adopt(Animal animal){ }
,是多态使用最多的场合。即便增加了新的子类,方法也无需改变,提高了扩展性符合开闭原则【针对扩展开放,对修改关闭】 - 弊端:在多态的场景下,我们创建了子类的对象,也加载了子类特有的属性与方法。但是由于声明为父类的引用,导致我们没有办法直接调用子类特有的属性与方法,如上面代码,
public void adopt(Animal animal){animal.eat()}
中只能写animal.eat()
重写的方法,不能单独使用animal.play()
或者animal.watchdoor()
向下转型(instanceof解决多态性的弊端)
类比于自动类型提升和强制类型转换:
比如:Person p1 = new Man();此时p1并不能调用子类Man中特定的方法earnMoney(),只能通过向下转型,Man m1 = (Man)p1;m1.earnMoney();
为了避免转型过程中把类的类型赋错,如:Person p2 = new Woman();Man m2 = (Man)p2;m2.earnMoney();
为词需要使用instanceof: a instanceof A判断a是否为A的一个实例关键字,即if (p2 instanceof Man){Man m2 = (Man)p2;m2.earnMoney();}
Object类以及clone()、finalize()
说明:类java.lang.Object是类层次结构的根类,即所有其他类的父类。每个类都使用Object作为超类。其没有声明属性,并提供了一个空参的构造器,但Object类提供了许多方法如下:
- clone():
Animal a2 = (Animal)a1.clone();//a1是a2的克隆Object对象,故要向下转型
- finalizae(): 临死之前说出你的遗言
Object中的equals()
适用范围:除了基本数据类型(用的是==),任何引用数据类型都可以使用
java.lang.Object类中equals()的定义:public boolean equals(Object obj){retur(this == obj);
其他的子类的equals()方法可能重写了,故不同的类中的equals()的方法的作用不相同,比如String、File、Data和包装类等。
子类使用说明:
- 自定义的类在没有重写Object中equals()方法的情况下,调用的就是Object类中声明的equals()。比较两个对象引用地址是否相同。(或者比较两个对象是否指向了堆空间中同一个对象实体),因此想实现比较自定义类实体相等的equal()方法需要自己重写【如下代码块】或者IDEA自动实现;
public boolean equals(Object obj){
if(this == obj){//如果obj与正在创建的对象同地址,则为真
return true;
}
if(obj instanceof User){//因为java的多态性Object obj可以==new User();故要判断obj是否属于User类
User user = (User)obj;//判断obj属于User类后,通过向下转型一边obj可以使用子类User中的一些特有的方法与属性值
return this.age ==user.age&&this.name.equals(user.name);//此时的equals方法是String的,不是目前自定义的
- 对于像String、File、Data和包装类等,它们都重写了Object类中的equals()方法,用于比较两个对象的实体内容是否相同。
- 像
Object中的toString()
- 自定义的类中,在没有重写Object类的toString()的情况下,默认的是调用Object类中的toString()方法,默认返回的是当前对象的地址值。
- 像String、File、Data或包装类:它们都重写了Object类的toString(),在调用toString()时,返回当前对象的实体内容。
总结:习惯上,开发中对于自定义的类在调用toString()时,也希望显示其对象的实体内容,而非地址值,故我们需要重写Object类中的toString()方法或者自动生成。
注意:println()方法会调用本类的toString()方法,要是本类没有重写toString方法,println()会打印地址值,如果重写了,则println()会打印本类实例对象实体值。
面向对象编程(高级)
static(静态的)
目的以及作用:使类的所有实例对象能够共享,比如中国人这个类能够共享中国国籍这个静态属性;在使用静态方法时也能够省略创建实例(new)这个步骤。
static修饰的结构:属性、方法、代码块、内部类;
- 修饰属性:使用static修饰的成员变量:静态变量、类变量(可以通过类名.属性直接使用);不使用static修饰的成员变量:非静态变量、实例变量
- 静态变量与实例变量的区别:
——个数:静态变量在内存空间中只存在一份,被类的多个对象所共享;实例变量被类的每一个实例都保存着。
——内存位置:静态变量在jdk6之前存放在方法区,jdk7之后存放在堆空间;实例变量存放在堆空间的对象实体中
——加载时机:静态变量随着类的加载而加载;实例变量随着对象的创建而加载
——消亡时机:静态变量随类的消亡而消亡;实例变量随对象的消亡而消亡;
- 修饰方法:使用static修饰方法:类方法(可以通过类名.静态方法直接使用)、静态方法,随着类的加载而加载;
注意:静态方法能够调用静态属性和静态方法,但是不能够调用非静态结构(易知)
单例设计模式与main()
单例设计模式含义:采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
思路**:将类的构造器的访问权限设置为private**,这样,就不能用new操作符在类的外部产生类的对象了,但在类的内部仍然可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
方法:
- 饿汉式:
public class BankTest{
public static void main(String[ ] args){
Bank bank = Bank.getInstance();
}
class Bank{
//类的构造器必须要私有化,才能让外部类不能随便创建实体对象
private Bank(){}
//在类的内部创建当前类的实例对象,相当于该类的属性,必须要用static修饰,才能被下面的静态方法调用
private static Bank instance = new Bank();
//因为构造器已经私有化了并且外部类已经不能创建实例了,所以必须使用静态方法调用实体对象
public static Bank getInstance()
return instance;
}
}
- 懒汉式:
private static Bank instance = new Bank();
变成private static Bank instance = null;并把instance = new Bank()放在getInstance()方法内,此时要判断
instance是否为空
代码块
作用:用来初始化类或者对象信息(即初始化类或对象的成员变量)
代码块的修饰:只能使用static进行修饰,根据是否用static修饰分为静态代码块和非静态代码块
- 静态代码块:在类中的样子:static{ };随着类的加载而执行;由于类的加载只会执行一次,进而静态代码块的执行也只会执行一次;作用是用来初始化类的信息;内部可以声明变量、调用属性或者方法、编写输出语句等操作;静态先于非静态;静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法)
- 非静态代码块:在类中的样子:{ };随着实例对象的创建而执行,每当创建当前一个实例对象,非静态代码块就会执行一次;用来初始化对象的信息;内部可以声明变量、调用属性或者方法、编写输出语句等操作;非静态代码块既可以调用非静态的结构也能调用静态的结构
类中属性赋值的位置及过程
位置以及顺序:默认初始化---->显示初始化、代码块中初始化—>构造器中初始化—>有了对象后可以通过对象.属性或者对象.方法(即set与get方法)
final(用来修饰类、方法、变量)
具体说明:
- final修饰类表示此类不能被继承,表示此类不能扩展了。
- final修饰方法表示此类方法不能被重写了,表示子类方法以及健壮了。
- final既可以修饰成员变量也可以修饰局部变量。此时的变量就变成了常量,意味着一旦赋值就不可以更改了。
——final修饰成员变量:此时的成员变量可以显示赋值、代码块赋值和构造器赋值其他的地方则不能赋值
——final修饰局部变量:方法内声明的局部变量,在调用局部变量时,一定需要赋值,而且一旦赋值就不可以更改了。
——final与static共同修饰的属性被称为全局常量
抽象类与抽象方法(abstract)
设置抽象类的目的:随着继承关系的逐渐深入,子类会变得越来越具体,而父类则更加一般,更加抽象,因此我们通常会使用子类来创建实例对象,但是由于多态性和继承性,父类并不能被摒弃,故可以通过abstract将父类改成不能创建实例对象的抽象类,如以下代码:
abstract class GeometricObject{//几何图形,不知道使三角形还是其他的图形
//求面积(只考虑提供方法的声明,因为没有具体的形状故没办法提供方法体,所以,此方法适合声明为抽象类
//求周长(同上)
}
class Circle extends GeometricObject{
//求面积(必须要重写或实现父类中的抽象方法)
//求周长(同上)
抽象方法:用abstract修饰的方法,只有方法的声明,没有方法体比如:public void A();
只有声明父子类共有方法的功能,没有具体的实现,具体的实现需要通过子类的重写实现
注意:
- 抽象类使包含构造器的,因为子类对象实例化的时候,需要直接或间接的调用父类的构造器的;
- 抽象类中可以没有抽象方法,但是有抽象方法所在的类,一定是抽象类;
- 子类必须要重写父类中所有的抽象方法之后方可实例化,否则,此子类仍然是个抽象类
- abstract不能修饰属性、构造器、代码块
- abstract不能与哪些关键词共用:private(private修饰的方法不能重写,但abstract修饰的方法必须重写);static(静态方法能通过类直接调用,而abstract声明的方法不能被调用,只能声明);final(不能继承,而abstract必须要继承重写)
接口(interface类比于抽象父类)
理解:接口的本质是契约、标准、规范,就像我们的法律一样。指定后大家都需要遵循(例如type-c接口,你要是不是(不遵循)这种类型的接口,你将无法实现接口的充电功能);接口(interface)与抽象父类(abstract class)是很相似的,可比较记忆;满足接口(即implements 接口名)就可以使用接口的功能(即是接口的抽象方法)
内部结构的说明:
- 可以声明:
-——属性:必须使用public static final修饰
-——方法:jdk8之前:声明抽象方法,修饰为public abstract可以省略,故接口可以看作抽象类;jdk8:声明静态方法、默认方法;jdk9:声明私有方法 - 不可以声明:构造器(故不能也不需要创建对象)、代码块
例子:
public class InterfaceTest{
public static void main(String [ ] args){
}
}
interface Flyable{
//全局变量
public static final int MIN_SPEED=0;
//由于接口属性的默认方式,故public static final可以省略
int MIN_SPEED=0;
//抽象方法
public abstract void fly();
//由于接口方法的默认方式,故publi abstract可以省略
}
接口与类的关系:实现关系(class A implements B即要A实现B的方法)
格式:class A extends SuperA implements B,C{ };
A相较于SuperA来讲,叫做子类,A相较于B,C来讲,叫做实现类
abstract class Plane implements Flyable{}//该Plane类要满足该接口要么使用abstract修饰,要不像下面代码一样重写方法
class Bullet implements Flyable,Attackable{
public void fly(){
//实现该方法
}
}
说明:类可以实现多个接口;类针对于接口的多实现,一定程度上弥补了类的单继承的局限性;类必须将实现的接口中的所有抽象方法都重写(或者实现),方可实例化,否则,此实现类必须声明为抽象类。
接口与接口的关系:继承并且可以多继承
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
class DD implements CC{
}
接口的多态性:接口名 变量名 = new 实现类对象;(类比于继承的多态性);代码与继承的多态性高度重合可看上面
扩展:创建接口匿名实现类的对象:new 接口名字(){重写方法}可以代替匿名实现类
//本来是USB usb1 = new 接口实现类
USB usb1 = new USB(){//由于实现类是匿名的,故先用new USB()顶替者,但USB接口是abstract的不能创建对象的,故必须要临时重写方法,所以要加{}
方法的重写
}.实现类的方法
类.方法(usb1)//类.方法(接口类型)
注意:以上匿名实现类的对象创建的写法,继承性也可以写哦,并且十分常见,必须理解看懂
内部类
定义:将一个类A定义在另一个类B中,里面的那个类A就称为内部类,类B就称为外部类
使用内部类的原因:当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其它地方单独使用,那么整个内部的完整结构B最好使用内部类,遵循“高内聚,低耦合”的面向对象开发原则
内部类的分类(参考变量的分类):
- 成员内部类:直接声明在外部类的里面
——使用static修饰的:静态成员内部类(与外部类相关)
——不适用static修饰的:非静态成员内部类(与外部类的对象相关)
理解:
——从类的角度上来说:拥有作为类的全部特性
——从外部类的成员的角度来看:在内部可以调用外部类的结构,比如:属性、方法等【具体实现看下面的Bird类的代码】;能使用封装性中所有的修饰符;可以使用static修饰
实例的创建如下代码: - 局部内部类:声明在方法内、构造器内、代码块内的内部类
——匿名的局部内部类
——非匿名的局部内部类
举例:
public class OutclassTest{
public static void main(String [ ] args){
Person.Dog dog = new Person.Dog();//静态成员内部类对象的创建
Person p1 = new Person();
Person.Bird bird = p1.new Bird();//非静态成员内部类对象的创建
}
}
class Person{//外部类
in age;
String name;
//静态成员内部类
static class Dog{
}
//非静态成员内部类
**class Bird{**
String name;
public void show(String name){
print(age);//直接调用外部类Person成员age,其中省略了Person.this.age
print(name);//打印形参变量name
print(this.name);//打印本内部类Bird成员name
print(Person.this.name);//打印外部类Person成员name
}
}
public void method(){
//局部内部类
class InnerClass1{
}
}
public Person(){
class InnerClass1{
}
}
{
class InnerClass1{
}
}
}
枚举类
定义:本质上也是一种类,只不过这种类的对象是有限的、固定的几个,不能让用户随意创建。
开发中的建议:开发中,如果针对于某个类,其实例是确定个数额,则推荐将此类声明为枚举类;如果枚举类的实例只有一个的时候,则可以看作是单例的实现方式。
举例:
……………………………………………………………………jdk 5.0之前…………………………………………………………
public class SeasonTest{
System.out.println("Season.SPRING");//通过类直接来调用对象,要打印出来下面必须要重写toString方法,我省略掉了
}
class Season(){
//声明当前类的对象的实例变量,对象创建好之后,变量不能再改变了,故用final来修饰
private final String seasonName;
private final String seasonDesc;
//由于对象的个数是有限的,故外界不能随便创建对象了,所以构造器要变成私有的
private Season(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName(){
return seasonNamel
}
public String getSeasonDesc(){
return seasonDesc;
}
//创建类的实例对象,因为构造器私有了,故只能在类的内部创建固定的对象了,外部要调用实例则必须通过类来调用,所以必须要加上static修饰
public static Season SPRING = new Season("春天","春暖花开");
public static Season SUMMER = new Season("夏天","夏日炎炎");
重写toString()
……
}
…………………………………………………………………………jdk 5.0………………………………………………………………
enum Season{
//必须在枚举类的开头声明多个对象。对象之间使用,隔开
SPRING("春天","春暖花开"),SUMMER("夏天","夏日炎炎");
//声明当前类的对象的实例变量,对象创建好之后,变量不能再改变了,故用final来修饰
private final String seasonName;
private final String seasonDesc;
//由于对象的个数是有限的,故外界不能随便创建对象了,所以构造器要变成私有的
private Season(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName(){
return seasonNamel
}
public String getSeasonDesc(){
return seasonDesc;
}
不需要重写toString(),默认会println会打印对象的值
}
……………………………………………………………………………………………………………………………………………………………
//每个枚举类对象没有具体的值,就可以直接省略如下,枚举类对象名就是一个值。
enum Season{
SPRING,SUMMER;
}
枚举类的一些方法:
- Season.SPRING.name():返回对象的名称也就是SPRING,【非静态方法,用对象调】
- Season[ ] values = Season.values()://Season里面已经有对象了,不需要再new了,直接通过values()赋值,可以再利用循环把数组values[ ]打印出来,【静态方法,用类来调】
- Season.valueOf(objName):返回枚举类中名称为objName的枚举类对象,【静态方法,用类来调】
- ordinal():返回当前枚举对象的次序号,从0开始,【非静态方法】
枚举类实现接口的操作:
——枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。
——枚举类中的每个对象都可以重写接口的方法:像下面的代码:
SPRING("春天","春暖花开"){
public void show(){
//重写接口方法
}
},
SUMMER("夏天","夏日炎炎"){
public void show(){
//重写接口方法
}
};
Annotation注解
【用处:框架 = 注解 + 反射 +设计模式】
理解:
——注解(Annotation)是从JDK5.0开始引入的,以“ ‘@注解名’ ”在代码中存在。
——Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值,这些信息被保存在Annotation的“name = value”对中。
——注解可以在类编译、运行时进行加载,体系那不同的功能。
注解的应用场景:生成文档的相关注解;在编译时进行格式检查(JDK内置三个基本的注解);跟踪代码依赖性,实现替代排至文件功能
Java基础涉及到的三个常用的注解:
——’@Override‘:限定重写父类方法,该注解只能用于方法【可以用来检查重写的方法是否写正确】
——’@Deprecated‘:【用于表示所修饰的元素(类、方法等)已过时。】 通常是因为所修饰的结构危险或者存在更好的选择
——‘@SuppressWarnings’:抑制编译器警告
元注解:对现有的注解进行解释说明的注解(有四种基本的元注解)
——‘@Target‘:用于表示自己修饰的注解能够修饰哪些结构
——’@Retention‘:用于表示声明周期的
——‘@Documented’
——’@Inherited‘
单元测试(Junit)
单元测试:可以省略main方法,直接对每个单元(方法)单独运行测试,不受其他方法的影响,具体见链接:单元测试方法以及流程和单元测试直接办法
包装类
使用包装类的原因:因为许多类方法的形参和泛型只针对对象类型,而不能使用基本数据类型;因此为了使得基本数据类型的变量具备引用数据类型变量的特征(多态、继承、封装),我们给各个基本数据类型的变量都提供了对应的包装类。
基本数据类型对应的包装类类型:
基本数据类型与包装类之间的转换:
-
为什么需要转换:
——因为许多类方法的形参和泛型只针对对象类型,而不能使用基本数据类型;因此为了使得基本数据类型的变量具备引用数据类型变量的特征(多态、继承、封装),我们给各个基本数据类型的变量都提供了对应的包装类。
——对于包装类来讲,既然我们使用的是对象,那么对象是不能进行数值运算的,为了能够使用这些运算,就需要将包装类的对象转换为基本数据类型的变量。 -
如何相互转换:
//第一种方式
int i1 = 10;
Integer ii1 = new Integer(i1);//此时ii1已经称为i1转换后的对象了
int i1 = ii1.intValue();//包装类数据类型转换成基本数据类型
i1 = i1+1;
…………………………………………………………………………………………………………………………………………………
//第二种方式(推荐)
int i1 = 10;
Integer ii1 = Integer.valueof(i1);
int i1 = ii1.intValue();//包装类数据类型转换成基本数据类型
i1 = i1+1;
…………………………………………………………………………………………………………………………………………………
//第三种方式(jdk5.0支持,更加方便)
int i1 = 10;
Integer ii1 = i1;//自动实现
Integer ii1 = i1+1;
int i1 = ii1;//自动实现
String与基本数据类型、包装类的转换
基本数据类型和包装类转换成String类型的:String.valueOf()或者【基本数据类型、包装类+“ ”】;反过来就是Xxx.parseXxx()【Xxx是基本数据类型首字母大写】
//基本数据类型和包装类转换成String类型的,第一种方式
int i1 = 10;
String str = String.valueOf(i1);//基本数据类型转换成String
Integer ii1 = i1;
String str = String.valueOf(i1);//包装类转换成String
//基本数据类型和包装类转换成String类型的,第二种方式
Stirng str = i1+"";
……………………………………………………………………………………………………………
//String转换为基本数据类型和包装类,第一种方式
String str = '123';
int i1 = Integer.parseInteger(str);