idea快捷键
- alt+enter:自动补全
- ctrl+d:复制当前内容到下一行
- ctrl+f5:程序再运行一次
- 100.for:自动生成循环一百次的for循环语句
- 数组名.fori:自动生成正着循环遍历数组的语句
- 数组名.forr:自动生成倒着循环遍历数组的语句
- fori+tab键:自动生成for循环
- alt+insert:自动生成类中常用的方法
- ctrl+h:显示继承树
- ctrl+i:快速生成抽象方法的实现
- ctrl+alt+T:可以选择自动生成try、catch、finally代码块
- ctrl+alt+L:自动格式化代码
- ctrl+p:查看方法需要的参数
- ctrl+alt+M:选中代码段,可以自动抽取方法
- shift+f6:变量的批量修改,先选中要修改的内容,再按组合键,修改时下面相同内容也会跟着做出相应修改
- 按住鼠标滚轮不松,可以竖着选中,或者按住alt不松,用鼠标左键选中
- 插件PTG:安装后可以右键点击ptg to JavaBean,直接生成标准的JavaBean
- ctrl+alt+v:自动生成定义变量的左边代码
- ctrl+b:跳转到源码
- ctrl+alt+t:选中代码块,按组合键可以选择相应的语句将代码块包裹起来
- ctrl+n:搜索
- ctrl+shift+u:全部变为大写
注释
Java中有三种注释:
1. 单行注释://,快捷键Ctrl+/
2. 多行注释:/* */,快捷键Ctrl+shift+/
3. 文档注释:/**
*@Description Hello
*@Author ***
*/
其中@是描述符
Java数据类型
Java是一门强类型语言,即变量的使用要严格符合规定,所有变量要先定义后才能使用
Java数据类型分为两大类,基本类型和引用类型
1. 基本类型
- 整数类型
- byte:一字节,-128--127
- short:两字节,-32768--32767
- int:四字节
- long:八字节
long a=30L;//long类型后面要加个L
整数类型拓展:
二进制0b;八进制0;十六进制0x
//不同进制赋值
int i = 10;
int i1 = 010;//八进制
int i2 = 0x10;//十六进制赋值
- 浮点类型
- float:四个字节
float a=50.1F;//float类型后面要加个F
- double:八个字节
浮点数拓展:
//float和double类型的两个值相同的数,他们的比较结果的布尔值是false
float a = 0.1;
double b = 1.0/10;
//变量a和b的比较结果是false
//因此银行业务中,表示钱不用float或者double,而是用BigDecimal(数学工具类)表示
- 字符类型
- char:两个字节
字符拓展:
//字符在内存中存放的都是相应字符的数值,ASCII码或者Unicode码(表示方式:'\u0061')
- boolean类型:占一位,只有true和false两种
2. 引用类型
类、接口、数组
类型转换
低------------------------------------------------------------------------>高
byte,short,char->int->long->float->double
- 自动类型转换:优先级由低到高会自动转换
- byte、short、char这三种数据类型进行运算的时候会自动转换成int类型
- 强制类型转换:优先级从高到低需要前置类型转换
double a = 1.5;
int b = (int)a;//此时b为1
注意:
- 不能对布尔值进行转换
- 不能把对象类型转换成不相干的类型
- 转换后可能会丢失精度或者溢出
- JDK7新特性:数字之间可以用下划线分割,即10_0000_0000
变量
Java变量是程序中最基本的存储单元,包括变量名,变量类型和作用域。
type varname [=value][{,varname[=value]}];
//数据类型 变量名 = 值;可以用逗号隔开来声明多个同类型变量
变量命名要满足命名规则:字母、下划线、$开头,不能用标识符命名
变量作用域
- 类变量
- 实例变量
- 局部变量
public class Variable{
static int allClicks=0; //类变量
int age;
String name;
//如果不进行初始化会有默认值,int的默认值为0,String的默认值为null
String str = "hello world";
public void method(){
int i = 0; //局部变量
}
}
常量
常量:初始化之后就不能再改变的值
final 常量名 = 值;(常量名一般为大写字符)
final PI = 3.1415926;
变量的命名规范
运算符
- 自增、自减运算符
int a = 1;
int b = a++;//表示先把a的值赋给b,然后a再加一
/* 等同于
int b =a;
a = a+1;
*/
int c = ++a;//表示的a的值先加1,然后再把a的值赋给c
/* 等同于
a = a+1;
int c = a;
*/
- 逻辑运算符
注:该类运算符中存在一种短路现象,即如果运算符左边的值就已经能决定该表达式的值了,那么就不再计算该运算符右边的表达式了。
int c = 5;
boolean d = (c<4)&&(c++<4);
System.out.println(d);//输出false
System.out.println(c);
//输出5,表示并没有执行(c++<4)这个表达式,因为逻辑运算符左边的表达式已经可以决定整个表达式的值了
- 位运算符
- ^为异或
- 移位运算符的效率很高,因此有时可以通过以为操作来实现乘方运算,左移一位相当于乘2,右移一位相当于除2
- 三元运算符
- 表达式1?表达式2 :表达式3
表示如果表达式1的值为真,那么整个表达式的值为表达式2的值,否则为表达式3的值
- 扩展赋值运算符
- +=、-+、*=、/=、%=这些运算符在参与运算时,编译器都会自动加上一个强制类型转换,相当于:
short a = 1;
short b = 2;
a += b;//相当于a = (short)(a+b);
//因为short、byte、char类型相互参与运算时,都会先提升为int类型
包机制
//语法格式
package pkg1.[pkg2[.pkg3..];//必须写在程序的最开头
/*包的命名格式一般是公司域名倒置
如:com.baidu
*/
//导入要使用的包
import package pkg1.[pkg2[.pkg3..];
JavaDoc
javadoc是用来生成api文档的,需要在程序中写好文档注释
在命令行执行:javac 参数 程序文件名称
javadoc -encoding UTF-8 -charset UTF-8 Doc.java
#-encoding UTF-8 -charset UTF-8为参数,使中文字体可以展示出来
/**
*@author xxx
*version 1.0
*since 1.5
*/
public class Doc{
String name;
/**
*@author xxx
*@param name
*@return
*/
public String test(String name){
return name;
}
}
在idea中生成api文档的方法:
- 点击IntelliJIDEA顶部的Tools菜单,选择GenerateJavaDoc选项
- 接下来在底部的Locale输入框配置语言和编码集,语言用zh_CN,代表中文,其他的输出和控制塔一致
- 接下来在底部的Locale输入框配置语言和编码集,语言用zh_CN,代表中文,其他的输出和控制塔一致
- 注意:生成的文档里面会有很多 .html 文件 正常
用户交互Scanner
Java提供了一个工具类,用来获取用户的输入。java.util.Scanner
基本语法:
Scanner s = new Scanner(System.in);
s.close(); //用完关闭
通过Scanner类的next()和nextline()方法获取输入的字符串,在读取钱一般要使用hasNext()与hasNextline()判断是否还有输入的数据。
next():
- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符前遇到的空白,next()方法会自动将其去掉
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符
- next()不能得到带有空格的字符串
nextline():
- 以Enter作为结束符,即返回输入回车前的所有字符
- 可以获得空白
类似还有输入整数或者其他类型的方法,如
nextInt()、nextFloat()
顺序结构
程序按顺序依次执行
选择结构
if语句:
if(表达式){
//如果表达式为true要执行的语句
}else if(){
//反之执行的语句
}.....
switch语句:
switch case语句判断一个变量与一系列值中某个值是否相等,每个值为一个分支
switch(expression){
case value:
//语句
break;//可选,用来表示结束
case value:
//语句
break;//可选
//可以有任意数量的case语句
default://可选,都不满足时执行的语句
//语句
}//括号也表示结束
switch语句中的变量类型可以是:
- byte、short、int、char、枚举、String(通过转换成哈希值实现的)
- case标签必须为字符串常量或者字面量
注:
在switch结构中的case分支下要写break语句,如果表达式与case分支的标签相匹配,但是该case分支里没有break语句,那么程序会顺序执行后面的case分支,直到遇到break语句或者到达default分支,或者遇到“}”才结束。
jdk12的新特性:
书写switch结构的格式有变化
int number = 1;
switch(number){
case 1 ->{
}
case 2 ->{
}
case 3 ->{
}
default ->{
}
}
/*
这种格式可以省略书写break语句,用{}代替了,不会出现case穿透的现象,并且case后面的语句如果只有一句,那么大括号也可以省略。
*/
int number = 1;
switch(number){
case 1 -> System.out.println("一");//只有一句
case 2 ->
case 3 ->
default ->
}
/*如果switch语句中的内容想要赋值给某个变量,也可以简化代码*/
String str = switch(number){
case '0' -> "";
case '1' -> "一";
default -> "";
};
循环结构
while循环:
while(表达式){
//循环语句
}
//表达式为真时一直循环下去
do while循环:
do{
//循环语句
}while(布尔表达式)
while和do while的区别:
- while先判断后执行,do while先执行后判断
- do while至少被执行一次
for循环:
for(初始化;布尔表达式;更新){
//循环语句
}
//不写布尔表达式,默认为真,也就是死循环
增强for循环:
主要用于数组或集合的遍历
for(声明语句 : 表达式){
//代码语句
}
//例
int[] nums = {1,2,3,4,5};
for(int x:nums){
System.out.println(x);
}
注:
声明语句:声明新的局部变量,该变量的类型必须和数组类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值维数组的方法。
Random类
用来生成随机数,如:
import java.util.Random;
Random r = new Random();
int number = r.nextInt(100);//表示生成0--99之间的任意的一个随机数,包头不包尾,包左不包右
break continue
break用于强行退出循环,不执行循环中剩余的语句
continue语句,用于终止本次循环,接着执行下一次循环
带标签的break continue
break outer:中断当前循环并跳出标签所指的循环
continue outer:重新跳到标签的位置,并开始标签之后声明的循环
使用带标签的 break / continue 的原因是:为了跳出或重新开始多层嵌套循环。
方法
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
方法的定义和调用
方法包含一个方法头和方法体:
- 修饰符:可选,告诉编译器如何调用该方法,定义该方法的访问类型
- 返回值类型:方法可能会返回值,用来说明返回值的类型,没有返回值用void修饰
- 方法名:方法的实际名称,和形参列表共同决定一个方法
- 参数类型:参数列表中的每个参数的类型
- 形参:方法定义时说明的,用来接收方法调用时传递的实参值
- 实参:调用方法时实际传给方法的数据
- 方法体:方法体包含具体的语句,定义该方法的功能
修饰符 返回值类型 方法名(参数类型 参数名,参数类型 参数名,...,...){
...
方法体
...
return 返回值;
}
方法调用:
- 调用方法:对象名.方法名(实参列表)
- Java支持两种调用方法的方式,根据方法是否返回值来选择
- 当方法返回一个值的时候,方法调用通常被当做一个值。例如:
int a = max(30,40);
- 如果方法返回值是void,方法调用一定是一条语句
System.out.println("Hello World");
注:Java中都是值传递,没有引用传递
方法的重载
- 重载就是在一个类中,有相同的函数名称,但形参不同的函数
- 方法重载的规则:
- 方法名必须相同
- 参数列表必须不同(个数不同、或类型不同、或参数排列顺序不同等)
- 方法的返回类型可以相同也可以不同
- 仅仅返回类型不同不足以成为方法的重载
- 实现理论:
- 方法名称相同时,编译器会根据调用方法的参数个数、参数类型等逐个去匹配,如果匹配失败,则编译器报错。
命令行传递参数
如果希望在运行程序时给程序传递消息,需要考传递命令行参数给main()函数实现。
public class Demo{
public static void main(String[] args){
for(int i = 0; i < 10; i++){
System.out.println("args["+ i +"]:" + args[i] );
}
}
}
可变参数
- JDK1.5开始,Java支持传递同类型的可变参数给一个方法
- 在方法声明中,在指定参数类型后加三个点(...)
- 一个方法中只能指定一个可变参数,他必须是方法的最后一个参数。任何普通参数必须在它之前声明。
public static void printMax(double... numbers){
if(numbers.length == 0){
System.out.println("No argument passed!")
rutrun;
}
double result = numbers[0];
for(int i=1; i<numbers.length; i++){
if(numbers[i]>result){
result = numbers[i];
}
}
System.out.println("This max value is " + result);
}
数组
数组的定义
- 数组是想同类型数据的有序组合
- 可以通过数组的下标来访问数组中的每个元素
数组声明创建
声明数组的语法
dataType[] arrayRefVar;//首选的方式
dataType arrayRefVar[];//效果相同,但不是首选方式
用new操作符来创建数组:
dataType[] arrayRefVar = new dataType[arraySize];
数组元素是通过索引访问的,数组索引从0开始
获取数组长度:arrays.length
声明在栈区,实例在堆区
数组的初始化
- 静态初始化
int[] a = new int[]{1,2,3};//静态初始化的完整格式
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};
- 动态初始化
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
- 默认初始化
数组是引用类型,他的元素相当于累的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量的方式被隐式初始化
- 数组地址值
public class HelloWorld {
public static void main(String[] args) {
double[] array = new double[]{1.1,2.2};
System.out.println(array);//[D@1b6d3586
}
}
/*
[D@1b6d3586的含义
[:代表是一个数组
D:代表数组的类型是double
@:分隔符(固定格式)
1b6d3586:数组真正的地址,十六进制的
*/
- 动态初始化和静态初始化的区别
动态初始化:手动指定数组长度,由系统给出默认初始化值
静态初始化:手动指定数组元素,系统会根据元素个数,计算出数组的长度
数组的四个基本特点
- 其长度是确定的。数组一旦被创建,他的大小就是不可以被改变的。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组边界
数组的大小是固定的,因此超过数组的大小会报错
- 数组下标的合法区间:[0,length-1],如果越界就会报错
ArrayIndexOutOfBoundsException:数组下标越界异常
数组的使用
- For-Each循环的方式来便利数组
- 数组当作方法的形参
- 数组作为方法的返回值类型
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其中的每个元素都是一个一维数组
int a[][] = new int[2][5];//数组a可以看成是一个2行5列的矩阵
Arrays类
- 数组工具类java.util.Arrays,可以用其中的方法来操作数组
- Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,从而可以不用使用对象来调用
- 常用的功能:
- 给数组赋值:fill()方法
- 给数组排序:sort()方法
- 比较数组中的元素是否相等:equals()方法
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找
面向对象
面向过程:按步骤一步一步的去完成,适合处理较为简单的问题
面向对象:将特点类似的众多对象归为一类,从而产生多个类,然后再对这些类下面的细节进行面向过程的分析,适合多人协作的复杂问题。
面向对象编程(Object-Oriented Programming,OOP)
面向对象三大特征:封装、继承、多态
类和对象的关系
类是一种抽象数据类型,是对某一类事物的整体描述,并不代表一个具体的事物。
对象就是类的一个具体实例。
创建与初始化对象
- 使用new关键字创建对象
- 使用new关键字创建对象时,除了会分配内存空间,还会给创建好的对象进行默认初始化,并且调用类的构造方法(构造器)。
Person p = new Person();//此时变量p就指向了一个Person类型的实例
构造方法
构造方法是在创建对象时必须要调用的,可以在构造方法中写初始化信息,且其有两个特点:
- 构造方法名必须和类名相同
- 没有返回值类型,也不能写void
无参构造方法:
没有参数的构造方法,在创建类时如果不主动编写,编译器会自动为该类生成一个无参构造方法。
public class Person{
String name;
public Person(){
//无参构造方法
}
}
有参构造方法:
带参数的构造方法,如过在类中编写,则需要显式的写出无参构造方法,否则会报错。
public class Person{
String name;
public Person(){
//无参构造方法
}
public Person(String name){//有参构造方法
this.name = name;
}
}
封装
开发程序要求高内聚低耦合,因此需要封装,将更多的信息隐藏起来,不需要暴露给用户。
例如使用get()/set()方法来操作类中的属性
public class Student{
private String name;//私有属性不能用对象来直接访问
public String getName(){//通过公有属性的get方法来获得私有属性的值
return this.name;
}
public void setName(name){//通过公有属性的set方法来设置私有属性的值
this.name = name;
}
}
get/set的作用
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 增加系统可维护性
封装的原则
对象代表什么,就得封装对应的数据,并提供数据对应的行为
标准的JavaBean
- 类名需要见名知意
- 成员变量使用private修饰
- 提供至少两个构造方法
- 无参构造方法
- 带全部参数的构造方法
- 成员方法
- 提供每一个成员对应的setXxx()/getXxx()方法
- 如果还有其他行为,也需要写上
继承
概述
一个类可以通过继承另一个类,来获得另一个类的公有和保护属性的方法和属性,通过extends关键字表示,这连个类一个称为父类,一个称为子类
public class Person{
public String name;
protected int age;
private char sex;
}
public class Student extends Person{
public int id;
}
//在main方法中
Student s = new Student();
/*
此时对象s只能访问父类Person中的公有和保护的属性和方法,不能访问父类以及自身类中的私有的属性和方法
*/
Java中只有单继承没有多继承,但是java中有接口,一个类可以实现多个接口,相当于变相的实现了多继承
super关键字
super代表父类,类似于this代表当前的对象
public class Person{
private String name;
public Person(){
System.out.println("Person");
}
public void say(){
System.out.println("说了一句话");
}
}
public class Student extends Person{
public Student(){
System.out.println("Student");
super.say();//在子类中使用super关键字调用父类的方法
}
}
//在main方法中
Student s = new Student();
/*
创建完对象之后,会按顺序输出
Person
Student
说了一句话
因此可以得出结论,在创建子类对象时,编译器会自动在子类的无参构造方法中的最开始调用父类的无参构造方法,即在子类的无参构造方法中不主动去调用父类的无参构造方法,编译器也会自动去完成;但是:如果显式的在子类的无参构造方法中调用父类的构造方法,必须要写在子类构造方法的第一行,如
public class Student extends Person{
public Student(){
super();//调用父类的构造方法
System.out.println("Student");
super.say();//在子类中使用super关键字调用父类的方法
}
}
*/
super注意点:
1. super调用父类的构造方法时只能在本类构造方法的第一行
1. super只能出现在子类的方法或者构造方法中
1. super和this不能同时调用构造方法,因为这两个都要求在构造方法的第一行
和this的区别:
- 代表的对象不同
this:当前对象
super:父类对象的引用
2.前提
this:没有继承也能使用
super:只能在继承条件下才能用
3.构造方法
this():本类的构造方法
super():父类的构造方法
重写
子类可以对父类中方法的实现进行重新编写,但是子类方法的名字、类型、返回值类型、参数列表都要和父类的方法相同
public calss A{
public static void test(){
System.out.println("A->test");
}
}
public calss B extends A{
public static void test(){
System.out.println("B->test");
}
}
//main方法里
A a = new B();//父类的引用可以指向子类
a.test();
B b = new B();
b.test();
/*
输出
A->test
B->test
结论:虽然在子类中对父类中的test方法进行了重新编写,但是这个test方法是静态方法,静态方法在类加载时就已经创建了,因此当用对象实例去调用时,调用的还是当前对象所在类中的方法
*/
public calss A{
public void test(){
System.out.println("A->test");
}
}
public calss B extends A{
public void test(){
System.out.println("B->test");
}
}
//main方法里
A a = new B();//父类的引用可以指向子类
a.test();
B b = new B();
b.test();
/*
输出
B->test
B->test
结论:在子类中对父类中的test方法进行了重新编写,但是创建的对象实例是子类的类型,因此在调用时只会调用子类中已经重写了的方法
*/
重写:需要有继承关系,在子类中重新编写父类的方法!
1. 方法名必须相同
1. 参数列表必须相同
1. 修饰符:范围可以扩大,但是不能缩小;public>protected>default>private
1. 抛出异常,范围可以缩小但是不能扩大;
为什么要重写?
- 父类的功能,子类不一定需要,或者不一定满足。
多态
概述
- 同一方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的,但是指向该对象的引用类型是不确定的
public class Perrson{
public void say(){
System.out.println("person");
}
}
public class Student extends Perrson{
public void say(){
System.out.println("student");
}
public void eat(){}
}
//在main方法里
//父类可以指向子类的对象,多态的表现
Student S1 = new Student();
Person S2 = new Student();
Object S3 = new Student();
s1.say();
s2.say();
/*
输出结果:
student
student
*/
s1.eat();
/*s2.eat();会报错,因为S2是Person类型的引用,在Person类中并没有eat方法,因此需要把Person类型的引用转换成Student类型才能使用eat方法,如 */
((Student) S2).eat();
/*
结论:对于父类和子类中都有的方法,实例对象最后会调用子类中重写的方法,如果是子类中有的而父类中没有的方法,那么父类类型的引用无法调用,需要强置类型转换之后才能调用
*/
instanceof 、类型转换
instanceof关键字
instanceof关键字可以判断某个类型的引用是否指向某个具体类型的对象
规则:父类可以指向子类
//Student Teacher 都是Person的子类
Objcet object = new 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
类型转换
子类可以直接转换成父类类型,父类类型需要强制类型转换成子类类型
printf
使用printf打印输出时,可以使用占位符,如:
System.out.printf("%s你好啊","张三");//输出:张三你好啊
static关键字总结
被static关键字修饰的属性或方法都是静态的,这种属性和方法都属于静态的,被存放到静态内存区,并并不属于任何一个对象,它是属于整个类的,当类被加载时,这些静态方法和属性就被创建了,因此可以通过类名来调用这些方法,而非静态方法就不可以,因此非静态方法在没有创建对象的时候是不存在的,同样在编写类中方法时,在静态方法中也不能调用非静态方法和属性,因为在创建静态方法的时候,非静态方法还不存在,也就无法在静态方法中调用了。
局部(匿名)代码块和静态代码块
public class Person{
{
//局部代码块,用来赋初始值
//变量只能在这里面使用,用完就释放了
System.out.println("匿名代码块");
}
static {
//静态代码块,类加载时就存在了,只执行一次
System.out.println("静态代码块");
}
public Person(){
System.out.println("构造方法");
}
}
//mian方法中
Person p = new Person();
/*
输出结果:
静态代码块
匿名代码块
构造方法
结论:
创建对象时,先加载静态代码块,然后是匿名代码块,最后是静态方法
*/
静态导入包:导入类中的静态方法或属性
import static java.lang.Math.random;
import static java.lang.Math.PI;
注意:通过final修饰的类就不能被继承了
抽象类
- abstract修饰符可以用来修饰类也可以用来修饰方法,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
- 抽象类中可以没有抽象方法,但是有抽象方法的类必须要声明为抽象类
- 抽象类不能用new关键字来创建对象,它是让子类来继承的
- 抽象方法,只有方法的声明,没有方法的实现,它是让子类来实现的
- 子类要继承抽象类,那么就必须要实现抽象类中的所有抽想象方法,否则也要声明成抽象类
public abstract calss Person{//抽象类
public void run(){}
public abstract void say();//抽象方法
}
注:抽象类虽然不能实例化对象,但是它也可以有构造方法构造方法的作用有两个,一个是初始化变量,另外一个是实例化对象,抽象类不能实例化,还可以做另外一个作用。
接口
接口的定义和实现
接口:只有方法的定义,没有方法的实现,关键字interface
public interface UserServices{
[public static final] int AGE = 10;
[public abstract ]void run(String name);
void insert(String name);
void delete(String name);
void update(String name);
void query(String name);
}
接口都有实现类,关键字为implements;(一个类可以实现多个接口),实现类中必须实现接口中的全部方法
public class UserServicesImpl implements UserServices{
public void insert(String name){}
public void delete(String name){}
public void update(String name){}
public void query(String name){}
}
接口中成员的特点
- 成员变量:只能是常量,默认修饰符:public static final
- 构造方法:没有
- 成员方法:只能是抽象方法,默认修饰符:public abstract
- JDK7以前:接口中只能定义抽象方法
- JDK8的新特性:接口中可以定义有方法体的方法
- JDK9的新特性:接口中可以定义私有方法
- 如果多个接口中有重名的方法,只需要重写一个重名方法即可
接口和接口之间可以单继承,也可以多继承,当实现已经继承父接口的子接口时,需要重写父接口和子接口中的所有抽象方法
JDK8以后在接口中新增的方法
- 允许在接口中定义默认方法,需要使用default关键字修饰
作用:解决接口升级的问题
接口中默认方法的定义格式
- 格式:public default 返回值类型 方法名(参数列表){}
范例:public default void show(){}
接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
- public 可以省略,default 不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类必须对该方法进行重写
- 允许在接口中定义静态方法,需要用static修饰
接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表){}
- 范例:public static void show(){}
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public 可以省略,static 不能省略
JDK9新增的方法
可以在接口中定义私有方法
- 格式1:private 返回值类型 方法名(参数列表){}
- 范例:private void show(){}
- 格式2:private static 返回值类型 方法名(参数列表){}
- 范例:private static void show(){}
接口的应用
- 接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
- 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
适配器设计模式
设计模式是一套反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让别人容易理解、保证代码可靠性、程序的重用性。
简单理解:设计模式就是各种套路
- 适配器设计模式:解决接口与实现类之间的矛盾问题
- 当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以用适配器的设计模式
- 书写步骤:
编写中间类xxxAdapter,实现对相应的接口
对接口中的抽象方法进行实现
让真正的实现类继承中间类,并重写需要用的方法
内部类
概述
内部类就是在一个类的内部定义一个类,内部类表示的事物是外部类的一部分,内部类单独出现没有任何意义
举例:在A类的内部定义B类,B类就称为内部类
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有成员
- 外部类要访问内部类的成员,必须创建对象
内部类的分类:成员内部类、静态内部类、局部内部类、匿名内部类
成员内部类
什么是成员内部类
- 写在成员位置的,属于外部类的成员
- 成员内部类可以被一些修饰符所修饰,比如:private、默认、protected、public、satatic等
- 在成员内部类里面,JDK16之前不能定义静态变量,JDK16之后才可以定义静态变量
获取内部类的对象
- 方式一:在外部类中编写方法,对外提供内部类的对象。(一般内部类被private修饰)
public calss Outer{
String name;
private calss Inner{
}
public Inner getInstance(){
return new Inner();
}
}
public class Test{
public class static void main(String[] args){
Outer o = new Outer();
System.out.println(o.getInstance());
}
}
//输出Outer$Inner@4eec777
//输出的意思是某个外部类中的内部类的对象的地址
- 方式二:直接创建
格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
范例:Outer.Inner oi = new Outer().new Inner();
public class Outer{
private int id;
public void out(){
System.out.println("这是外部类的方法");
}
public calss Inner{
public void in(){
System.out.println("这是内部类的方法");
}
public void getId(){//内部类可以访问外部类的私有成员
System.out.println(id);
}
}
}
//在main中创建内部类的对象
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
成员内部类如何获取外部类的成员变量
public calss Outer{
private int a = 10;
calss Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(Outer.this.a);//10;因为编译器会在内部类的字节码文件中创一个Outer类型的this$0常量,保存的是外部类对象的地址
System.out.println(this.a);//20
System.out.println(a);//30
}
}
}
静态内部类
用static关键字修饰的成员内部类就是静态内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态方的需要创建对象
- 创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
- 调用静态方法的格式:
外部类名.内部类名.方法名();
- 调用非静态方法的格式:
先创建对象,用对象调用
public class Outer{
private int id;
public void out(){
System.out.println("这是外部类的方法");
}
public static calss Inner{//静态内部类
public void in(){
System.out.println("这是内部类的方法");
}
}
}
局部内部类
- 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量,但是局部内部类不能用public、private、protected等关键字修饰
- 外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
public class Person{
public void say(){
public class Inner{
}
}
}
匿名内部类
- 匿名内部类:隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
- 匿名内部类的格式:
new 类名或者接口名(){
重写方法;
} - 格式的细节
- 包含了继承或实现,方法重写,创建对象
- 整体就是一个类的子类对象或者接口的实现类对象
- 使用场景
- 当方法的参数是类或者是接口时,以接口为例,可以传递这个接口的实现对象
- 如果实现类只要使用一次,就可以用匿名内部类简化代码
- 匿名类
public interface UserServices{
void run();
}
//在main方法中
UserServices userservice = new UserServices(){//匿名类
@override
public void run(){
}
};
异常
字符串
概述
- String时Java定义好的一个类。定义在java.lang中,所以使用的时候不需要导包
- Java程序中的所有字符串文字都被看做是String类的对象
- 字符串不能改变。一旦创建不可更改
创建String对象的两种方式
- 直接赋值
String name = "123"
- new关键字
//使用String类型的构造方法
//public String() 创建空白字符串不包含任何内容
//public String(String original) 根据传入的字符串,创建字符串对象
//public String(char[] chs) 根据字符数组,创建字符串对象
//public String(byte[] chs) 根据字节数组,创建字符串对象
String的内存模型
通过直接赋值的方式创建的字符串是保存在”串池“(StringTable)中的,在jdk7之后,串池保存在堆内存中,在jdk7之前保存在方法区中。串池的特点是不会重复创建相同的字符串,当直接赋值创建的两个字符串的内容相同时,则不会开辟新的空间,而是利用相同的空间,即两个String类型的引用指向相同空间。
通过new关键字创建的字符串,每new一次都会开辟新的空间。
字符串比较
- 用==号比较
==号比较的是内容,因此当两个基本数据类型的变量比较时,比较的就是这两个变量的内容;当两个引用类型的变量比较时,比较的也是引用类型变量的内容,即地址值。
- String提供的字符串比较方法
- boolean equals(要比较的字符串) 完全一样才是true,否则为false
- boolean equalsIgnoreCase(要比较的字符串) 忽略大小写比较
字符串中的方法
- charAt(索引):返回指定索引上的字符
- String substring(int beginIndex,int endIndex):返回指定范围的子串(包左不包右,包头不包尾)
- 重载方法String substring(int beginIndex):默认截取到末尾
- String replace(旧值,新值):字符串替换,只有方法的返回值才是替换后的结果
StringBuilder类
概述
StringBuilder可以看成是一个容器,创建之后里面的内容是可变的
作用:提高字符串的操作效率
因为StringBuilder是Java改写的类,Java在底层对它做了一些特殊处理,因此打印对象不是地址值而是属性值
StringBuilder的构造方法
- public StringBuilder():创建一个空白可变字符串对象,不含有任何内容
- public StringBuilder(String str):根据字符串的内容,来创建可变字符串对象
StringBuilder常用方法
- public StringBuilder append(任意类型):添加数据,并返回对象本身
- public StringBuilder reverse():反转容器中的内容
- public int length():返回长度(字符出现的个数)
- public String toString():通过toString()就可以把StringBuilder转换为String
StringJoiner类
概述
- StringJoiner和StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的
- 作用:提高字符串的操作效率,而且代码编写特别简洁,但是目前市场上很少有人使用
- JDK8出现的
构造方法
- public StringJoiner(间隔符号):创建一个StringJoiner对象,指定拼接时的间隔符号
- public StringJoiner(间隔符号,开始符号,结束符号):创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号
成员方法
- public StringJoiner add(添加的内容):添加数据,并返回对象本身
- public int length():返回长度(字符出现的个数)
- public String toString():返回一个字符串(该字符串就是拼接之后的结果)
字符串相关的底层原理
- 字符串拼接的底层原理
- 等号右边没有变量
public class Test{
public static void main(String[] args){
String s = "a"+"b"+"c";
}
}
拼接时没有变量,都是字符串,触发字符串的优化机制。在编译的时候就已经是最终的结果了
- 等号右边有变量
JDK7的拼接方式
public class Test{
public static void main(String[] args){
String s1 = "a";
String s2 = s1+"b";
String s3 = s2+"c";
}
}
例如字符串s2 = new StringBuilder().append(s1).append("b").toString;
JDK8的拼接方式
public class Test{
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s3 = "c";
String s4 = s1 + s2 + s3;
}
}
拼接时会先预估字符串的长度,这里预估为3,然后创建一个长度为3的数组,数组里面存入"a"、"b"、"c",再把这个整体变为一个字符串
结论:
如果很多字符串变量拼接,不要直接+。在底层会创建多个对象,浪费时间,浪费性能。
- StringBuilder源码分析
- 默认创建一个长度为16的字节数组
- 添加的内容长度小于16,直接存
- 添加的内容大于16会扩容(原来的容量*2+2=34)
- 如果扩容之后还不够,以实际长度为准
集合
集合和数组的区别
- 大小不同
- 数组一旦定义,大小就固定了
- 集合不需要指定大小,可以自动扩容
- 存储类型不同
- 数组可以存基本数据类型,也可以存引用数据类型
- 集合只能存引用数据类型,要存基本数据类型,需要将其变成对应的包装类
基本数据类型的包装类
基本数据类型 | 包装类 |
byte | Byte |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
ArrayList类
泛型
集合中可以存放不同数据类型的数据,但是可以用泛型来限定集合中的元素类型,如:
ArrayList list = new ArrayList();//没有进行限定
ArrayList<String> list = new ArrayList<String>();//JDK7以前的写法,限定数据类型为String
ArrayList<String> list = new ArrayList<>();//JDK7以后的写法,限定数据类型为String
System.out.println(list);
/*
输出:
[]
因为此时创建的对象是ArrayList的对象,而ArrayList是java已经写好的一个类,这个类在底层不是地址值,而是集合中存储的内容,在显示的时候会用[]把所有的数据进行包裹
*/
ArrayList的成员方法
- boolean add(E e):添加元素,返回值表示是否添加成功
- boolean remove(E e):删除指定元素,返回值表示是否删除成功
- E remove(int index):删除指定索引的元素,返回被删除元素
- E set(int index,E e):修改指定索引下的元素,返回原来的元素
- E get(int index):获取指定索引的元素
- int size():集合的长度,也就是集合中元素的个数
三种不同功能的类
- Javabean类
用来描述一类事物的类。如,Student、Teacher、Dog、Cat
- 测试类
用来检查其他类是否书写正确,带有main方法的类,是程序的入口 - 工具类
不是用来描述一类事物的,而是帮我们做一些事情的类,里面的方法大都是静态的,并且构造方法的属性是私有的。
包
包就是文件夹。用来管理不同功能的Java类,方便后期代码维护
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。
导包的规则(import)
- 使用同一个包中的类时,不需要导包
- 使用java.lang包中的类时,不需要导包
- 其他情况需要导包
- 如果同时使用两个包中的同名类,需要用全类名
final关键字
用final修饰:
- 方法:表明该方法是最终方法,不能被重写
- 类:表明该类时最终类,不能被继承
- 变量:叫做常量,只能被赋值一次
常量
常量一般作为系统配置信息,方便维护,提高可读性
常量的命名规范:
- 单个单词:全部大写
- 多个单词:全部大写,单词之前用下划线隔开
细节:
final修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变
权限修饰符
- 权限修饰符:是用来控制一个成员能够被访问的范围的
- 可以修饰成员变量,方法,构造方法,内部类
分类:
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
private | 可以 | |||
空着不写 | 可以 | 可以 | ||
protected | 可以 | 可以 | 可以 | |
public | 可以 | 可以 | 可以 | 可以 |