文章目录
- 前言
- JAVASE
- 三大版本
- JDK,JRE,JVM
- 编译运行java文件
- 编译型和解释型
- 注释
- 标识符
- 数据类型
- 类型转换
- 变量
- 常量
- 命名原则
- 运算符
- 包机制
- javadoc
- 流程控制
- 条件
- 循环
- break与contiune
- switch case
- goto与标签
- 数组
- 方法
- 内存分析
- 多维数组
- Array类
- 1)int binarySearch(type[] a, type key)
- 2)int binarySearch(type[] a, int fromIndex, int toIndex, type key)
- 3)type[] copyOf(type[] original, int length)
- 4)type[] copyOfRange(type[] original, int from, int to)
- 5)boolean equals(type[] a, type[] a2)
- 6)void fill(type[] a, type val)
- 7)void fill(type[] a, int fromIndex, int toIndex, type val)
- 8)void sort(type[] a)
- 9)void sort(type[] a, int fromIndex, int toIndex)
- 10)String toString(type[] a)
- 稀疏数组
- 面向对象OOP
- 类与对象的关系
- 封装
- 继承
- 重写
- 多态
- instanceof和类型转换
- static关键字详解
- 抽象类
- 接口
- 内部类
- 异常
前言
本人回顾学习javase总结的一些内容,根据kuangshen的视频路线来进行回顾。第一次发文如有不足望指出。
JAVASE
三大版本
1.javaSE:标准级(桌面应用,控制台开发)
2.javaME:嵌入式开发(手机,小家电)
3.javaEE:E企业级开发(web端,服务器开发)
JDK,JRE,JVM
JDK:Java Development Kit(开发者工具,包含了JRE和JVM)
JRE:Java Runtime Environment(运行时环境)
JVM:Java Virtual Machine(虚拟机)
编译运行java文件
windows平台下编译,cmd内输入javac 文件名.java
运行CLASS文件 java 文件名。
编译型和解释型
编译型:有的编程语言要求必须提前将所有源代码一次性转换成二进制指令,也就是生成一个可执行程序(Windows 下的 .exe),比如C语言、C++、Golang、Pascal(Delphi)、汇编等,这种编程语言称为编译型语言,使用的转换工具称为编译器。
解释型:有的编程语言可以一边执行一边转换,需要哪些源代码就转换哪些源代码,不会生成可执行程序,比如 Python、JavaScript、PHP、Shell、MATLAB 等,这种编程语言称为解释型语言,使用的转换工具称为解释器。
注释
//单行注释
/*
多
行
注
释
*/
标识符
Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
- List item
*Ⅰ所有的标识符都应该以字母,美元符,或者下划线开始
Ⅱ首字母之后可以是字母,美元符、下划线、数字的任何字符组合
Ⅲ不能使用关键字作为变量名或方法名
Ⅳ大小写敏感
数据类型
强类型数据
要求变量的使用要严格符合规定,所有变量都必须先定义后使用
弱类型语言
例子JavaScript
java的数据类型分为两大类
1.基本类型
2.引用类型
整数扩展
二进制0B
八进制0
十六进制0x
浮点数扩展
float f=0.1f;
double d=1.0/10;
System.out.println(f==d);
//结果并不相等
float d1=21212121212121f;
float d2=f1+1;
System.out.println(d1==d2);
//结果相等
浮点数是有限,离散,舍入误差
浮点数实际上就是以更少的有效数字为代价换取更大的表示范围,而且有效数字的位数与数值大小无关(先不考虑非规范数这种奇葩),因此数值越大,精度越差。与之相对的,定点数的精度固定,有效数字的位数与数值大小相关。
最好完全避免使用浮点数进行比较。精细业务最好使用BigDecimal类表示。
字符扩展
编码Unicode 2字节 可以表示0-65536 ,所有的字符本质还是数字。
转义字符为常见为\ +字符具体可以查看制表符
String str1=new String("hello World");
String str2=new String("hello World");
System.out,println(str1==str2);//结果为false
String str3 = "hello World";
String str4 = "hello World";
System.out,println(str3==str4);//结果为true
内存问题。
布尔值拓展
太简单了不写了
类型转换
精度排列
byte,short,chat->int->long->float->double
强转(类型)变量:高到低
//溢出案例
int i=128;
byte b=(int)i;//结果等于-128
//精度案例
System.out.println((int)23.7);//输出23
自动(类型)转换:低到高
*不能对布尔值进行转换
*不能把对象类型转化为不相干类型
*转换时会产生内存溢出或精度问题
*数字之间可以用下划线分割(JDK7新特性)
*计算时所用到的变量是啥,计算结果将会自动转化为变量的数据类型
int money=10_0000_0000;
int year=20;
int total=monry*year;
long total2=money*year;//二者答案一致,都为溢出
long total3=money*(long)year;//改正后不存在溢出问题
变量
java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域
*每个变量都要有类型,类型可以是基本类型,也可以是引用类型。
*变量名必须是合法标识符。
*变量声明必须是一条完整的语句。
作用域
*类变量:加上static,从属于类,生命周期同类相同
*局部变量:必须声明和初始值
*实例变量:类的里面方法的外面,从属于对象,调用时要用new新建。
常量
*初始化之后就不允许再改变。用final声明。
命名原则
*所有变量、方法、类名,见名知意
*类成员变量:首字母小写和驼峰原则
*局部变量:首字母小写和驼峰原则
*常量:大写字母和下划线
*类名:首字母大写和驼峰原则
*方法名:首字母小写和驼峰原则
运算符
*算数运算符:+ - * / % ++ --(算数运算符的结果默认为当前变量类型中精度最高的类型)
*赋值运算符:=
*关系运算符:> < >= <= == != != instanceof(instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false)
A a = null;
B b = null;
boolean result;
result = a instanceof A;
System.out.println(result); // 结果:false null用instanceof跟任何类型比较时都是false
result = b instanceof B;
System.out.println(result); // 结果:false null用instanceof跟任何类型比较时都是false
*逻辑运算符:&& || !
*位运算符:& | ^ ~ >> << >>>
*条件运算符:? :
x?y:z//如果x==true则结果为y,否则结果为z
*扩展赋值运算符:+= -= *= /=
*字符串连接符:+
包机制
*为了更好的组织类,java提供了包机制,用于区分类名和名称空间。
package pkg1[. pkg2[. pkg3...]];
*一般为公司域名倒置作为包名
*导入包
import package1[.package2...].(classname|*);
javadoc
*javadoc命令是用来生成自己API文档的
*参数类型
@author 作者
@version 版本号
@param 参数名 描述 方法的入参名及描述信息,如入参有特别要求,可在此注释。
@return 描述 对函数返回值的注释
@deprecated 过期文本 标识随着程序版本的提升,当前API已经过期,仅为了保证兼容性依然存在,以此告之开发者不应再用这个API。
@throws异常类名 构造函数或方法所会抛出的异常。
@exception 异常类名 同@throws。
@see 引用 查看相关内容,如类、方法、变量等。
@since 描述文本 API在什么程序的什么版本后开发支持。
{@link包.类#成员 标签} 链接到某个特定的成员对应的文档中。
{@value} 当对常量进行注释时,如果想将其值包含在文档中,则通过该标签来引用常量的值。
示例
/**
*@author bluestone
*@version 1.0
*@since 1.8
*/
public class Doc{
String name;
/**
*@author bluestone
*@param name
*@return
*@throws Exception
*/
public String test(String name) throws Exception{
return name;
}
}
javadoc -encoding UTF-8 -charset UTF-8 文件名.java
*生成文档的dos命令
idea生成javaDoc步骤
第一步:在工具栏中找到 Tool,然后再子菜单中找到 Generate JavaDoc。
第二步:在生成文档上进行配置。
配置的简单介绍:
参数说明
1.Whole project:整个项目都生成文档
2.Custom scope 自定义范围。如下:
(1)project files 项目文件,
(2)project production files 项目产品文件,
(3)project test files 项目的测试文件, 未知范围,class hierarchy 类层
3.include test source 包含测试目录
4.include JDK and … 包含jdk和其他的第三方jar
5.link to JDK documentation…链接到JDK api
6.output directy 生成的文档存放的位置
(1)private、package、protected、public 生成文档的级别(类和方法)
(2)右边的Generate…是选择生成的文档包含的内容,层级树、导航、索引…
(3)再右边是生成的文档包含的内容信息,作者版本等信息
7.Locale 语言类型,zh-CN
8.Other command line arguments 其他参数
9.Maximum heep… 最大堆栈
我这里的配置如下:
说明:IntelliJ IDEA作为Java流行的编辑器, 其生成一些Javadoc会对中文乱码, 使用UTF-8编码即可. 这个常见的问题, 则需要生成时设置参数即可.
在 “Tools->Gerenate JavaDoc” 面版的 “Other command line arguments:” 栏里输入 :
传入JavaDoc的参数,一般这样写
-encoding UTF-8 -charset UTF-8
不然的话会报可能会报错误: 编码GBK的不可映射字符。
流程控制
用户交互Scanner
java.util.scanner可以获取用户输入
Scanner s=new Scanner(System.in);
通过Scanner类的next()与nextLine()方法获取输入的字符串,再读取前我们一般需要使用hasNext()与hasNextLine()判断是否还有输入的数据。
public static main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方式接收");
//判断用户有无输入字符串
if(scanner.hashNext()){
String str=next();
System.out.println(str);
}
//凡是属于IO流的类如果不关闭会占用资源。
scanner.close();
}
next与nextLine的区别
next():
*一定要读取到有效字符后才可以结束输入。
*对输入有效字符之前遇到空白,next()方法会自动将其去掉
*只有输入有效字符后才将其后面输入的空白作为分割符或者结束符
*next()不能得到带有空格的字符串
nextLine():
*以Enter作为结束符
*可以获得空格
一般使用方式
String str = scanner.nextLine();
//判断输入数据类型(整型举例)
if(scanner.hasNextInt()){
i=scanner.nextInt();
System.out.println("整型数据"+i);
}
else{
System.out.println("你输入的数据不是整型");
}
条件
略
循环
for:
for(int x:numbers){
System.out.println(x);//遍历数组
}
while
do while
break与contiune
*break再任何循环语句的主体部分,均可用break跳出。(也可以在switch语句中使用)
*continue语句终止某次循环过程,接着执行下一次循环
switch case
switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}
- switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
- switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
- case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
- 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
- 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
- switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
goto与标签
java保留有goto关键字但是没有goto用法。有标签用法
outer:for(int i=1;i<100;i++){
for(int j=2;j<=i/2;j++){
if(i%j==0){
continue outer;
}
}
System.out.println(i+" ");
}
数组
首先必须声明数组变量,才能在程序中使用。
dataType[] arrayRefVar;//首选方法
dataType arrayRefVar[];
java使用new操作符来创建数组
dataType[] arrayRefVar = new dataType[arraysize];
获取长度
array.length
方法
定义
修饰符 返回值类型 方法名(参数类型 参数名...){
方法体
return 返回值;
}
重载
在一个类中,有相同的函数名称,但形式参数不同的函数。
规则:
*方法名称必须相同
*参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)
*方法的返回类型可以相同也可以不同
*仅仅返回类型不同不足以成为方法的重载
命令行传参
for(int i=0;i<args.length;i++){
System.out.println("args["+i+"]:"+args[i]);
}
控制台输入
java 具体包名 参数1 参数2 参数3 …
可变参数
JDK1.5开始,java支持传递同类型的可变参数给一个方法
*在声明方法中,在指定参数类型后面加一个省略号(…)
*一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。
public static void printMax(double... number){
return;
}
递归
递归包含两个部分
1.递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
2.递归体:什么时候需要调用自身方法。
例子:
public static void main(String[] args) {
int i=f(5);
System.out.println(i);
}
public static int f(int n){
if(n==0)return 1;
return n*f(n-1);
}
finalize() 方法
Java 允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象。
例如,你可以使用 finalize() 来确保一个对象打开的文件被关闭了。
在 finalize() 方法里,你必须指定在对象销毁时候要执行的操作。
finalize() 一般格式是:
protected void finalize() { // 在这里终结代码 }
关键字 protected 是一个限定符,它确保 finalize() 方法不会被该类以外的代码调用。
当然,Java 的内存回收可以由 JVM 来自动完成。如果你手动使用,则可以使用上面的方法。
内存分析
java内存
1.堆:
*存放new的对象和数组
*可以被所有线程共享,不会存放别的对象引用
2.栈
*存放基本变量类型(会包含这个基本类型变量的具体数值)
*引用对象的变量(会存放这个引用在堆里面的具体地址)
3.方法区
*可以被所有的线程共享
*包含了所有的class和static变量
三种初始化
1.静态初始化:创建+赋值
int[] a={1,2,3,4,5,6};
2.动态初始化:包含静态初始化
int[] a = new int[10];
b[0]=10;
多维数组
*多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每个元素都是一个一维数组。
int a[][] = new int[5][2];
Array类
*数组的工具类java.util.Arrays
*由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用
*查看JDK帮助文档
*Arrays类中的方法都是static修饰的静态方法,在使用时可以直接使用类名进行调用,而“不用”使用对象来调用
1)int binarySearch(type[] a, type key)
使用二分法查询 key 元素值在 a 数组中出现的索引,如果 a 数组不包含 key 元素值,则返回负数。调用该方法时要求数组中元素己经按升序排列,这样才能得到正确结果。
2)int binarySearch(type[] a, int fromIndex, int toIndex, type key)
这个方法与前一个方法类似,但它只搜索 a 数组中 fromIndex 到 toIndex 索引的元素。调用该方法时要求数组中元素己经按升序排列,这样才能得到正确结果。
3)type[] copyOf(type[] original, int length)
这个方法将会把 original 数组复制成一个新数组,其中 length 是新数组的长度。如果 length 小于 original 数组的长度,则新数组就是原数组的前面 length 个元素,如果 length 大于 original 数组的长度,则新数组的前面元索就是原数组的所有元素,后面补充 0(数值类型)、false(布尔类型)或者 null(引用类型)。
4)type[] copyOfRange(type[] original, int from, int to)
这个方法与前面方法相似,但这个方法只复制 original 数组的 from 索引到 to 索引的元素。
5)boolean equals(type[] a, type[] a2)
如果 a 数组和 a2 数组的长度相等,而且 a 数组和 a2 数组的数组元素也一一相同,该方法将返回 true。
6)void fill(type[] a, type val)
该方法将会把 a 数组的所有元素都赋值为 val。
7)void fill(type[] a, int fromIndex, int toIndex, type val)
该方法与前一个方法的作用相同,区别只是该方法仅仅将 a 数组的 fromIndex 到 toIndex 索引的数组元素赋值为 val。
8)void sort(type[] a)
该方法对 a 数组的数组元素进行排序。
9)void sort(type[] a, int fromIndex, int toIndex)
该方法与前一个方法相似,区别是该方法仅仅对 fromIndex 到 toIndex 索引的元素进行排序。
10)String toString(type[] a)
该方法将一个数组转换成一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号,
和空格隔开。
稀疏数组
第一行代表6行7列一共存在8个值。后面几行为某数具体在某位。
面向对象OOP
概念
面向过程
*步骤清晰简单,第一步做什么,第二步做什么…
*面向过程适合处理一些较为简单的问题
面向对象
*物以类聚,分类的思维模式
*适合处理复杂问题,多人对接
回顾方法
static是和类一起加载的声明之后创建类之后就直接存在。
静态方法:加static
不需要实例化,直接调用
非静态方法:
需要实例化
Student student = new Student();
student.say();
值传递和引用传递:
值传递不会改变方法外的值。
引用传递会改变。(一般引用的是对象)
类与对象的关系
类是一种抽象的数据类型,它是对一类事物整体描述/定义。
对象是抽象概念的具体实例
规范
一个项目规范使用一个Main方法。
创建与初始化对象
*使用new关键字创建对象的时候,除了分配内存,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
*类中的构造器也称为构造方法,是在创建对象的时候必须调用的,并且构造器有以下两个特点
1.必须和类的名字相同。
2.必须没有返回类型,也不能写void。
3.构造方法类诞生即存在。
public class Person{
String name;
//使用new的本质是调用构造函数
public Person(){//无参构造
//初始化实例值
this.name="bluestone";
}
public Person(String name){//有参构造,一旦定义了有参构造,无参就必须显示。
this.name="bluestone"
}
}
idea快捷键alt+insert快速生成构造器
封装
*程序设计追求“高内聚,低耦合”,高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量方法给外部使用
*通常。应静止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
*通常用get/set方法使用数据
继承
继承的本质是对某一批类的抽象,从而实现更好的建模
*extends是子类关键字
*Java中只有单继承没有多继承
*继承是类与类之间的一种关系,除了继承之外还有依赖,组合,聚合等。
*继承关系的两个类,一个为子类(派生类),另一个为父类(基类)
*object类
*super
*方法重写
*ctrl+h打开继承树
*java中所有的类都默认直接或这简介继承Object类
Super
Super可以调用父类中的方法和变量。
例如
Application.java
public class application{
public static void main(string[] args){
Student student = new Student();
student.test("1");
}
}
Person.java
public class Person{
protected String name = "3";
}
Student.java
public class Student extends Person{
private String name = "2";
public void test(String name){
System.out.println(name);//1
System.out.println(this.name);//2
System.out.println(super.name);//3
}
}
注意点
*super调用父类的构造方法,必须在构造方法中的第一个。
*super只能出现在子类的方法或构造函数中。
*super和this不能同时调用构造方法
VS this
代表的对象不同:
this:本身调用者这个对象
super:代表父类对象的应用
前提:
this:不需要继承
super:一定要继承条件
构造方法:
this();本类的构造
super();父类的构造
重写
*只有public修饰词修饰的方法可以重写
*需要有继承关系,子类重写父类方法。
1.方法名必须相同
2.参数列表必须相同
3.修饰符范围可以扩大:public>protected>default>protect
4.抛出的异常:可以被缩小但是不能扩大:ClassNotFoundException --> Exception(大)
重写,子类的方法和父类必要一致:方法体不同
多态
*即同一种方法可以根据发送对象的不同而采用多种不同的行为方式
*一个对象实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
public class Person{
public void run(){
System.out.println("run");
}
}
public class Student extends Person{
@Override
public void run(){
System.out.prnitln("son");
}
public void eat(){
System.out.println("ear");
}
}
public static void main(String[] srgs){
//可以指向的引用类型就不确定了:父类的引用指向子类
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
s2.run();
s1.run();
((Student)s2).eat();//子类重写了父类的方法,执行子类方法
s1.eat();
}
*多态是方法的多态,属性没有多态
*父类和子类,有联系类型转换异常!ClassCastException!
*存在条件:
1.继承关系,
2.方法需要重写,
3.父类引用指向子类对象!Father f1 = new Son();
*无法重写
1.static 方法,属于类,它不属于实例
2.final 常量;
3.private方法;
*多态是方法的多态,属性没有多态
instanceof和类型转换
public static void main(String[] srgs){
//可以指向的引用类型就不确定了:父类的引用指向子类
Object object = new Student();
Person person = new Person();
//Object>String
//Object>Person>Teacher
//Object>Person>Student
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
System.out.println(object instanceof Student);//True
System.out.println(object instanceof Person);//True
System.out.println(object instanceof Object);//True
System.out.println(object instanceof Teacher);//False
System.out.println(object instanceof String);//False
System.out.println(person instanceof Student);//True
System.out.println(person instanceof Person);//True
System.out.println(person instanceof Object);//True
System.out.println(person instanceof Teacher);//False
System.out.println(person instanceof String);//编译报错!没有关联
}
public static void main(String[] srgs){
Person student = new Person();
//student将这个对象转换为Student类型,这样我们才可以使用Student类型的方法!
Student student = (Student) student;
student.go();
//或者写为
((Student)student).go();
}
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型
3.把父类转换为子类,向下转型;强制转换
4.方便方法的调用,减少重复的代码
static关键字详解
在类中,使用 static 修饰符修饰的属性(成员变量)称为静态变量,也可以称为类变量,常量称为静态常量,方法称为静态方法或类方法,它们统称为静态成员,归整个类所有。
静态成员不依赖于类的特定实例,被类的所有实例共享,就是说 static 修饰的方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,Java 虚拟机就可以根据类名找到它们。
调用静态成员的语法形式如下:
类名.静态成员
注意:
- static 修饰的成员变量和方法,从属于类。
- 普通变量和方法从属于对象。
- 静态方法不能调用非静态成员,编译会报错。
静态变量
类的成员变量可以分为以下两种:
- 静态变量(或称为类变量),指被 static 修饰的成员变量。
- 实例变量,指没有被 static 修饰的成员变量。
静态变量与实例变量的区别如下:
1)静态变量
- 运行时,Java 虚拟机只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配。
- 在类的内部,可以在任何方法内直接访问静态变量。
- 在其他类中,可以通过类名访问该类中的静态变量。
2)实例变量
- 每创建一个实例,Java 虚拟机就会为实例变量分配一次内存。
- 在类的内部,可以在非静态方法中直接访问实例变量。
- 在本类的静态方法或其他类中则需要通过类的实例对象进行访问。
静态变量在类中的作用如下:
- 静态变量可以被类的所有实例共享,因此静态变量可以作为实例之间的共享数据,增加实例之间的交互性。
- 如果类的所有实例都包含一个相同的常量属性,则可以把这个属性定义为静态常量类型,从而节省内存空间。例如,在类中定义一个静态常量 PI。
静态方法
与成员变量类似,成员方法也可以分为以下两种:
- 静态方法(或称为类方法),指被 static 修饰的成员方法。
- 实例方法,指没有被 static 修饰的成员方法。
静态方法与实例方法的区别如下:
- 静态方法不需要通过它所属的类的任何实例就可以被调用,因此在静态方法中不能使用 this 关键字,也不能直接访问所属类的实例变量和实例方法,但是可以直接访问所属类的静态变量和静态方法。另外,和 this 关键字一样,super 关键字也与类的特定实例相关,所以在静态方法中也不能使用 super 关键字。
- 在实例方法中可以直接访问所属类的静态变量、静态方法、实例变量和实例方法。
静态代码块
静态代码块指 Java 类中的 static{ } 代码块,主要用于初始化类,为类的静态变量赋初始值,提升程序性能。
静态代码块的特点如下:
- 静态代码块类似于一个方法,但它不可以存在于任何方法体中。
- 静态代码块可以置于类中的任何地方,类中可以有多个静态初始化块。
- Java 虚拟机在加载类时执行静态代码块,所以很多时候会将一些只需要进行一次的初始化操作都放在 static 代码块中进行。
- 如果类中包含多个静态代码块,则 Java 虚拟机将按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。
- 静态代码块与静态方法一样,不能直接访问类的实例变量和实例方法,而需要通过类的实例对象来访问。
抽象类
*abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法那么该方法就是抽象方法;类同样
*抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类
*抽象类不能使用new关键字创建对象,是用来继承的
*抽象方法只有方法的声明,没有方法的实现,让子类来实现
*子类继承抽象类,那么就必须要实现抽象类里没有实现的抽象方法,否则也要声明为抽象类
*在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。
public abstract class Shape {
public int width; // 几何图形的长
public int height; // 几何图形的宽
public Shape(int width, int height) {
this.width = width;
this.height = height;
}
public abstract double area(); // 定义抽象方法,计算面积
}
public class Square extends Shape {
public Square(int width, int height) {
super(width, height);
}
// 重写父类中的抽象方法,实现计算正方形面积的功能
@Override
public double area() {
return width * height;
}
}
public class Triangle extends Shape {
public Triangle(int width, int height) {
super(width, height);
}
// 重写父类中的抽象方法,实现计算三角形面积的功能
@Override
public double area() {
return 0.5 * width * height;
}
}
public class ShapeTest {
public static void main(String[] args) {
Square square = new Square(5, 4); // 创建正方形类对象
System.out.println("正方形的面积为:" + square.area());
Triangle triangle = new Triangle(2, 5); // 创建三角形类对象
System.out.println("三角形的面积为:" + triangle.area());
}
}
接口
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!
*接口就是规范,定义的一组规则,体现了现实世界中的“如果你是…则必须能…”的思想。
*接口的本质是契约。
*声明接口的关键字是interface
定义接口
Java 接口的定义方式与类基本相同,不过接口定义使用的关键字是 interface,接口定义的语法格式如下:
[public] interface interface_name [extends interface1_name[, interface2_name,…]] {
// 接口体,其中可以包含定义常量和声明方法
[public] [static] [final] type constant_name = value; // 定义常量
[public] [abstract] returnType method_name(parameter_list); // 声明方法
}
对以上语法的说明如下:
- public 表示接口的修饰符,当没有修饰符时,则使用默认的修饰符,此时该接口的访问权限仅局限于所属的包;
- interface_name 表示接口的名称。接口名应与类名采用相同的命名规则,即如果仅从语法角度来看,接口名只要是合法的标识符即可。如果要遵守 Java 可读性规范,则接口名应由多个有意义的单词连缀而成,每个单词首字母大写,单词与单词之间无需任何分隔符。
- extends 表示接口的继承关系;
- interface1_name 表示要继承的接口名称;
- constant_name 表示变量名称,一般是 static 和 final 型的;
- returnType 表示方法的返回值类型;
- parameter_list 表示参数列表,在接口中的方法是没有方法体的。
注意:一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。
接口对于其声明、变量和方法都做了许多限制,这些限制作为接口的特征归纳如下:
- 具有 public 访问控制符的接口,允许任何类使用;没有指定 public 的接口,其访问将局限于所属的包。
- 方法的声明不需要其他修饰符,在接口中声明的方法,将隐式地声明为公有的(public)和抽象的(abstract)。
- 在 Java 接口中声明的变量其实都是常量,接口中的变量声明,将隐式地声明为 public、static 和 final,即常量,所以接口中定义的变量必须初始化。
- 接口没有构造方法,不能被实例化。例如:
public interface A {
publicA(){…} // 编译出错,接口不允许定义构造方法
}
一个接口不能够实现另一个接口,但它可以继承多个其他接口。子接口可以对父接口的方法和常量进行重写。例如:
public interface StudentInterface extends PeopleInterface {
// 接口 StudentInterface 继承 PeopleInterface
int age = 25; // 常量age重写父接口中的age常量
void getInfo(); // 方法getInfo()重写父接口中的getInfo()方法
}
例如,定义一个接口 MyInterface,并在该接口中声明常量和方法,如下:
public interface MyInterface { // 接口myInterface
String name; // 不合法,变量name必须初始化
int age = 20; // 合法,等同于 public static final int age = 20;
void getInfo(); // 方法声明,等同于 public abstract void getInfo();
}
实现接口
接口的主要用途就是被实现类实现,一个类可以实现一个或多个接口,继承使用 extends 关键字,实现则使用 implements 关键字。因为一个类可以实现多个接口,这也是 Java 为单继承灵活性不足所作的补充。类实现接口的语法格式如下:
<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name…]] {
// 主体
}
对以上语法的说明如下:
- public:类的修饰符;
- superclass_name:需要继承的父类名称;
- interface1_name:要实现的接口名称。
实现接口需要注意以下几点:
- 实现接口与继承父类相似,一样可以获得所实现接口里定义的常量和方法。如果一个类需要实现多个接口,则多个接口之间以逗号分隔。
- 一个类可以继承一个父类,并同时实现多个接口,implements 部分必须放在 extends 部分之后。
- 一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
下面的Sports接口被Hockey和Football接口继承:
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
接口的多继承
在Java中,类的多继承是不合法,但接口允许多继承。
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:
public interface Hockey extends Sports, Event
内部类
*内部类就是再类的内部在定义的一个类。分为
1.成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
class` `Circle {
``private` `double` `radius = ``0``;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance() {
return new Draw();
}
class`Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
}
}
}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。比如上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
2.静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
int a=10;
static int b = 5;
public Outter() {
}
static class Inner {
public Inner() {
System.out.println(a);
System.out.println(b);
}
}
}
3.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
*局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
4.匿名内部类
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段Android事件监听代码:
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
history_bt.setOnClickListener(new OnClickListener() {//匿名内部类使用
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。
private void setListener()
{
scan_bt.setOnClickListener(new Listener1());
history_bt.setOnClickListener(new Listener2());
}
class Listener1 implements View.OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
class Listener2 implements View.OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和static修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
异常
概念
1.检查性异常
最具代表性的是用户错误或问题引起的异常,这是程序员无法预见的。
2.运行时异常
运行时异常是可能被程序员避免的异常。可以在编译时被忽略。
3.错误
错误不是异常,是脱离程序员控制的问题,错误在代码中通常被忽略。
异常结构体系
*java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
*在java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception
Error
*error类由java虚拟机生成并抛出,大多数错误与代码编写者的操作是无关的。
*java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,JVM一般会选择线程终止。
*还有很多发生在JVM试图执行应用时,如类定义错误、连接错误。这些错误是不可察的,因为他在程序处理和控制的范围外,而且绝大多数是程序运行时不允许出现的状况。
Exception
运行时异常
*运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常
*非运行时异常是指 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如 IOException、ClassNotFoundException 等以及用户自定义的 Exception 异常(一般情况下不自定义检查异常)。
异常类型 | 说明 |
---|---|
ArithmeticException | 算术错误异常,如以零做除数 |
ArraylndexOutOfBoundException | 数组索引越界 |
ArrayStoreException | 向类型不兼容的数组元素赋值 |
ClassCastException | 类型转换异常 |
IllegalArgumentException | 使用非法实参调用方法 |
lIIegalStateException | 环境或应用程序处于不正确的状态 |
lIIegalThreadStateException | 被请求的操作与当前线程状态不兼容 |
IndexOutOfBoundsException | 某种类型的索引越界 |
NullPointerException | 尝试访问 null 对象成员,空指针异常 |
NegativeArraySizeException | 再负数范围内创建的数组 |
NumberFormatException | 数字转化格式异常,比如字符串到 float 型数字的转换无效 |
TypeNotPresentException | 类型未找到 |
ClassNotFoundException | 没有找到类 |
IllegalAccessException | 访问类被拒绝 |
InstantiationException | 试图创建抽象类或接口的对象 |
InterruptedException | 线程被另一个线程中断 |
NoSuchFieldException | 请求的域不存在 |
NoSuchMethodException | 请求的方法不存在 |
ReflectiveOperationException | 与反射有关的异常的超类 |
Error与Exception区别
- Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。
- Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。
处理机制
处理异常五个关键字
1.try
2.catch
3.finally
4.throw
5.throws
Java 的异常处理机制提供了一种结构性和控制性的方式来处理程序执行期间发生的事件。异常处理的机制如下:
- 在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。
- 对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理(一般在方法中使用)。
- 捕获异常的catch异常类型要从小捕获到大。
try {
逻辑程序块
} catch(ExceptionType1 e) {
处理代码块1
} catch (ExceptionType2 e) {
处理代码块2
throw(e); // 再抛出这个"异常"
} finally {
释放资源代码块
}
*idea代码生成快捷键Ctrl+Alt+D
throws 声明异常
当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。throws 具体格式如下:
returnType method_name(paramList) throws Exception 1,Exception2,…{…}
其中,returnType 表示返回值类型;method_name 表示方法名;paramList 表示参数列表;Exception 1,Exception2,… 表示异常类。
如果有多个异常类,它们之间用逗号分隔。这些异常类可以是方法中调用了可能拋出异常的方法而产生的异常,也可以是方法体中生成并拋出的异常。
使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。
自定义异常
使用java内置的异常类可以描述编程时出现的大部分异常。除此之外,用户还可以自定义异常。用户自定义异常类,只需要继承Exception类即可
在程序中使用自定义异常类,大体为一下步骤
1.创建自定义异常类。
2.在方法中通过throw关键字抛出异常对象。
3.如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获异常;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,进行下一步操作。
4.在出现异常方法的调用处捕获并处理异常。
实际使用
*处理运行时异常时,采用逻辑去合理规避同时辅助try-catch去处理
*在多重catch块后面可以加一个catch(Exception)来处理可能会被遗漏的异常
*对于不确定的代码也可以加上try-catch,处理潜在的异常
*切记不要简单的调用printStackTrace()去打印异常,要及时处理
*尽量添加finally语句块释放资源