Java基础
只做些简单笔记,供查看方便,日后还需慢慢填补
面向过程部分
Java概述(一些常识)
一些历史知识在此不作拓展,因为我背不下来,需要慢慢熏陶加上日后的学习,才能慢慢感悟(找借口)。
常用DOS命令
- dir 显示文件夹内容(directory)
- md 创建文件夹(make directory)
- rd 移除文件夹(remove directory)
- cd 进入指定目录
- cd.. 退回到上一级目录
- cd\ 退回到根目录
- del 删除文件
- exit 退出DOS命令行
Java语言的特点
- 面向对象性
两个要素:类、对象
三个特征:封装、继承、多态 - 健壮性
除去C/C++的指针
自动的垃圾回收(GC,garbage collection)机制,但是用力过猛也会内存泄漏、内存溢出 - 跨平台性
特有的JVM(Jav Virtual Machine)虚拟机,一次编译,到处运行。
跨平台原理:编译器把源文件编译为字节码文件(.class),JVM可以把字节码文件翻译为各个平台可以执行的机器码。显而易见,每个平台的JVM并不能通用。
环境搭建
放一个环境搭建的链接,不赘述了。
注释
单行注释://
多行注释:/* */
*文档注释*:(Java特有)
/**
@auther 指定java程序的作者
@version 指定源文件的版本
*/
Java基本语法
关键字、标识符
关键字
被Java赋予特殊意义的的字符串。
标识符
凡是可以自己定义名字的地方都是标识符。
标识符的定义规则:
- 数字、字母、下划线、$,数字不能打头
- 不可以使用关键字和保留字(保留字:Java现在并未使用,但是不可作为标识符。)
- Java严格区分大小写,长度无限制
- 支持中文,不能有空格,要做到见名知意
标识符命名规范:
- 包名:多个单词组成时,每个单词都小写
- 类名、接口名:每个单词首字母大写
- 变量名、方法名:第一个单词首字母小写,其余单词首字母大写
- 常量名:单词所有字母大写,单词与单词之间用“_”隔开
变量
变量的种类、每一种变量的特点、需要注意的几个小点、定义和使用变量要遵循的规则
整型
byte:1字节,-128~127
short:2字节,-32768~32767
int:4字节,-231~231-1(约21亿)
long:8字节,-263~263-1
注意:
- 声明long类型时,末尾加
l
或L
- 用常量初始化整型时,默认该常量是int类型
- 如果需要整型,通常使用int类型
浮点型
float:4字节,-3.403E38~3.403E38
double:8字节,-1.798E308~1.798E308
注意:
- 常量默认是double类型
- 声明float类型,要加
f
,或F
- 浮点类型数据通常使用double
字符型
char:2字节,可以参与整数运算(0~65535,没有负值)
初始化方法:
char c1 = 'a'; // 使用单引号包含单个字母
char c2 = 97; // 使用字母编号直接赋值,很少使用
char c3 = '\u0043'; // 使用Unicode编码
布尔型
boolean:通常认为是1字节,只有true
和false
注意:
- 实打实的true和false,不能用1、0什么的代替
String类型
字符串在这里要说的不多
字符串是引用数据类型,在和基本数据类型做运算的时候只能是连接操作。
// 要求在控制台打印出* *
System.out.println("* *"); // 可以
System.out.println('*' + '\t' + '*'); // 不可以,* \t 是char类型,会作整形运算,结果是整数
System.out.println('*' + "t" + '*'); // 可以
System.out.println('*' + '\t' + "*"); // 不可以,道理同上
String str = 4; // 不可以
String Str1 = 3.5f + ""; // 可以,打印结果是"3.5"
数据类型转换(重点)
自动类型提升:
容量小的类型和容量大的类型做运算时,会自动提升为容量大的类型,再做运算。
注意:
- 这里的容量指的是类型表示的数值范围,并不是所占字节数。
- byte、short、char参与的整型计算,会提升为int类型防止溢出
- 字符型向其他类型转换时,会先转换成对应字符的统一编码,然后再进行类型转换
byte b1 = 'b'; // 'b'被转为98,然后98向byte类型作转换,下面同理
byte b2 = (byte)'b';
short s1 = '\u8000';
short s2 = (short)'\u8000';
将数字类型转换成字符类型时,只使用整数的低 16 位
(浮点数类型将整数部分转换成字符类型)。
将字符类型转换成数字类型时,字符的统一码转换成指定的数值类型。
如果字符的统一码超出了转换成的数值类型的取值范围,则必须显性声明类型转换。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/java-interview-highlights/e24mo1/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
强制类型转换:
强制转换符:()
强制类型转换可能会造成数据精度损失
注意这种编码情况
long l1 = 123456; // 这里的123456是int,可以转换为long
long l2 = 1234567889424234; // 这里的常量超出int类型的范围,报错:过大的整数
long l3 = 1234567889424234L; // 后面加上L,就不会报错了
运算符
运算符的种类,要求会用,并且知道需要注意的几个细节,其中位运算符用的不多,主要用于看源码的时候
- 算术运算符:加、减、乘、除、取余、正负号、++、–、字符串连接
取余的结果符号和被除数一样;前++是先使用值作为表达式的值,再+1,–同理;字符串连接取决于+左右的运算成员有没有字符串。 - 赋值运算符:=、+=、-=、*=、/=…
赋值运算符(包括++、–,不包括=)不会改变变量的数据类型 - 比较运算符:> >= < <= == != instanceof
- 逻辑运算符:& && | || ! ^(逻辑与、短路与、逻辑或、短路或、非、异或)
短路运算:逻辑运算符左边的表达式已经足以判断出整个表达式的结果时,则不再进行右侧表达式的计算 - 位运算符:& | ~ ^ << >> >>>(与、或、非、异或、左移、右移、无符号右移)
左移:右边的空位补0,右移:左边的空位补符号位的值,无符号右移:左边空位补0 - 三元运算符:(条件表达式) ? (表达式1) : (表达式2)
条件表达式的结果是true执行表达式1,false执行表达式2
表达式1和2要求其数值类型要可以互相转换,否则不能通过编译;在if-else和三目运算符都可以使用的地方,尽量使后者,效率更高。
流程控制
分支结构:
// 结构一
if (条件) {
语句;
}
// ***********************************
// 结构二:二选一
if (条件) {
语句;
} else {
语句;
}
// ***********************************
// 结构三:n选一
if (条件) {
语句;
} else if(条件) {
语句;
} else if (条件) {
语句;
}... ...
else {
语句
}
针对条件表达式:
- 如果多个条件表达式范围不重合,则上下位置无所谓;
- 如果一个范围大,一个小,需要把小的放上面,否则可能执行不到小的;
- 如果有交集,需要看情况安置。
switch (表达式) {
case 常量1:
语句;
break; // 不是必须的,看需要添加。小心击穿效果即可。
case 常量2: // 可以合并
case 常量3:
语句;
break;
... ...
default: // 位置灵活,可随意更换
语句;
break;
}
- 击穿:表达式进入某一case,由于没加break,会直接往下执行进行不属于他的其他case,直到遇到break或者default。
- 表达式中支持的数据类型:
byte、short、char、int、枚举(JDK5新增)、String(JDK7新增) - case之后只能放常量,不能声明范围
- break并不强制,看需要求
- 多个case情况一样时,考虑合并
循环结构
// for循环
for (①;②;③) {
④;
}
// 执行顺序:①②④③②④③... ...④③②跳出
// while循环
①
while (②) {
③;
④;
}
// 执行顺序:①②③④②③④... ...③④② 跳出
// do-while循环
①
do {
②;
③;
} (④);
// 执行顺序:①②③④②③④... ...②③④跳出
循环有 两种跳出方式:
- 循环条件被破坏
- break语句
注意:
while循环和do-while循环千万不要忘记迭代条件,否则会出现死循环。
关键字break和continue
- break用于结束循环和switch语句
- continue用于结束本轮循环,直接进入下一轮循环
数组
数组的初始化
// 一维数组
// 静态初始化:创建数组和初始化数组同时进行
int[] arr1 = new int[]{1,2,3,4,5};
int[] arr2 = {1,2,3,4,5}; // 此为简写方式
// 动态初始化:创建数组和初始化数组分开进行
int[] arr3 = new int[5];
// 二维数组
// 静态初始化
int[][] arr4 = new int[][]{ {1,2,3}, {4,5,6}, {7,8,9} };
int[][] arr5 = { {1,2,3}, {4,5,6}, {7,8,9} };
// 动态初始化1
int[][] arr6 = new int[3][3];
// 动态初始化2
int[][] arr7 = new int[4][]; // 可不指定列数,但一定要指定行数
int[] arr8[] = new int[2][3]; // 也算是正确的定义
数组的特点
- 数组属于引用数据类型,元素可以是基本数据类型也可以是引用数据类型
- 数组一旦被创建其长度就被确定、不可更改
- 数组在内存空间中的地址是连续的,一整块的
数组的其他需要注意
- 元素有默认值
整型:0
浮点型:0.0
布尔型:false
字符型是:’\u0000’或0,而不是’0’
引用数据类型:null - 长度:arr.length
- 二维数组中,arr.length代表行数,arr[i].length是列数
- 二位数组默认初始化:
// 动态初始化1
int[][] arr6 = new int[3][3];
//外层存储的是内部一维数组的地址值,内层存储的是类型默认的值,和一维数组默认值一样。
// 动态初始化2
int[][] arr7 = new int[4][];
// 外层存储的地址值是null,此时不可以直接访问元素值。
数组常见算法(概述)
数据结构
- 数据结构指的是数据与数据之间的逻辑关系:集合、一对一、一对多、多对多
- 数据的存储结构:
线性表:顺序表(比如数组),链表、栈、队列
树形结构:二叉树
图形结构: … …算法
- 排序算法
- 搜索算法
-
针对数值型数组
最大值、最小值、平均数、总和 -
数组的赋值与复制
-
数组元素反转
数组中指定元素查找:
- 线性查找
- 二分查找
面向对象部分
类和对象
什么是类、对象
类是一类事物的抽象、总结,好比是生产零件的图纸;对象是具体的某一个事物,就像是生产出来的具体的某一个零件。
类中的成员:
- 属性
- 方法
- 构造方法
- 代码块
- 内部类
接下来
类的加载:执行java命令后,类中的信息会被加载到内存中的方法区,然后从main方法开始执行,new对象的时候,会在堆区中另开辟一块内存。
属性
属性的赋值方式,和赋值的先后顺序:
属性的默认值->显示初始化->代码块初始化->构造器初始化->成员方法 ->对象.属性赋值
成员方法
权限修饰符 返回值类型 方法名(参数列表) {
方法体;
}
- 形参:形式参数,相当于一个占位的变量,等待实参传入,它将带着实参的数值做运算。
- 实参:实际从方法外部传进来的具体数值。
实参的传入顺序要和形参列表一致才不会出错。 - 可变长参数:(String… args)
要放在形参列表的最后,且最多只能有一个可变长参数。对可变长参数的操作,可同数组。
// 以下程序输出结果是什么
public class Test {
public static void main(String[] args) {
Base base = new Sub();
base.show(1, 2, 3);
Sub s = (Sub)base;
s.show(1, 2, 3);
}
}
// 也就是判断下列情况是否算重写(Override),是算重写的
class Base {
public void show(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends Base {
public void show(int a, int[] arr) {
System.out.println("sub");
}
public void show(int a, int b, int c,) {
System.out.println("sub_2");
}
}
sub
sub_2
解释:
首先,可变长参数和数组在形参中,可视为相等(当然可变长参数有它自己的定义要求要遵守),所以show方法算重写。而以父类引用调用show方法时,编译时,使用父类的,运行时使用子类的,这里讨论的是重写的方法,而Sub类中的第二个show是重载,并不参与,所以第一个输出是sub;当父类类型转换成子类后,则是子类中两个show方法的选择,传入的参数正好是3个,匹配重载后的show,这种情况下会优先调用重载后的方法。
- 返回值类型:方法做完运算需要传递出去的数据,它的类型就是返回值类型。使用return关键字返回数据。 - 方法名:遵循标识符命名法则即可 - 权限修饰符: ![访问权限修饰符](https://img-blog.csdnimg.cn/20210207104312740.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMzIyNjA1,size_16,color_FFFFFF,t_70#pic_center) - 参数传递: 传递基本数据类型:值传递,方法内外的值互不干扰,即在方法内部修改该值,方法外部的变量值也不会被更改。 传递引用数据类型:同是值传递,可此次传递的是实际数据的内存地址(包含其类型),所以在方法内部修改数据会牵连到外部的引用,是真正地修改了堆区中的数据。 传递引用数据类型后,如果在方法内形参又转而指向别的地方,则再修改形参,则不会再影响实参。
构造方法
语法格式
访问权限修饰符 类名(形参列表){
构造体
}
- 构造器没有返回值类型
- 构造器的名字必须和类名一样
- 构造器是在创建对象时,做对象的初始化工作
构造器会在使用new
关键字时被调用,进行对象初始化。
this关键字
this关键字的作用:
- 用于区分属性的局部变量,this代表属性
class Person {
String name;
int age;
Person (String name, ing age){
this.name = name;
this.age = age;
}
}
- 用于方法中返回调用该方法的对象本身
- 写在构造器第一行,用于调用其他构造器
class Computer {
String name;
int price;
Computer (){
this("联想", 12345); // 需传入初始化参数
}
Computer (String name, int price){
this.name = name;
this.price = price;
}
}
关于方法的调用:
每当调用一次方法时,都会在内存中的栈区分配一个栈帧给这个方法,这个方法所需要的局部变量就会放在这个栈帧之中,当我们使用对象调用方法时,默认传入一个this参数,指向这个对象,this可以访问这个对象的属性、调用该对象的方法,就像是指向这个对象的一个引用(像是Person p = new Person中的p)。
方法重载(Overload)
相同的方法名、不同的形参列表。在对象调用同一个方法时,通过传入不同组合的实参,来决定具体调用哪个方法。这样可以简化方法的使用,并且不阉割方法的功能。
不同的形参列表体现在:形参的类型、个数、顺序不同
以下情况不属于方法重载(Overload):形参仅名字不同,返回类型不同。
构造块和静态代码块
构造块
class Person {
String name;
int age;
// 构造块
{
name = "张三";
age = 1;
}
public static void main(String[] args) {
Person p = new Person();
System.out.println(p.name + " " + p.age);
}
}
张三 1
静态代码块:
String name;
int age;
//静态代码块
static {
System.out.println("静态代码块");
}
// 构造块
{
System.out.println("构造块");
name = "张三";
age = 1;
}
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
}
静态代码块
构造块
构造块
静态代码块随着类的加载执行,类加载一次,它就执行一次,此后每new一个对象,便先执行普通的构造块,然后执行构造方法。
递归
指在自己的方法中调用自己,使用递归需要遵循以下三点:
- 要向着已知的方向走
- 写法不能变得更加复杂,而是简化写法
- 执行效率要高
//斐波那契数列的普通递归和尾递归
public class Fibonacci {
public static long fibonacci(long index) {
if (index <= 1) {
return index;
} else {
return fibonacci(index - 1) + fibonacci(index - 2);
}
}
// **************************************
public static long fibonacciTailRecursion(long index) {
return fibonacciTailRecursion(index, 0, 1);
}
public static long fibonacciTailRecursion(long index, int curr, int next) {
if (index == 0) {
return curr;
} else {
return fibonacciTailRecursion(index - 1, next, curr + next);
}
}
}
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/java-interview-highlights/e2y595/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
封装
封装的目的:维持代码的健壮性,并且掩盖代码的实现细节,让注意力集中在业务逻辑上。
实现步骤:
- 私有化属性
- 提供共有的get、set方法操作属性,并进行合理值得判断
- 在构造器中使用set方法初始化属性
继承
static 关键字
不可以乱用static关键字,必须是类所有对象共享的东西才可以使用。
static 属性
由普通的对象层级上升为类层级,创建出的对象共享这一个属性,而不是每创建一个对象就创建一个属性。static属性存放在方法区。
并且可以直接用类名.的方式直接访问(如果是public等可访问状态)。
static 方法
和static属性类似,可以直接使用类名.的方式调用,而不依赖于对象。(当然对象也可以调用,不过推荐用类名.的方式)
static方法中不可以访问非static的属性,不可以使用this关键字。因为此时可能没有对象被创建,static修饰的成员要以没有对象作为标准来设计。
单例模式
/*
没有做封装处理,便于测试
*/
class Singleton {
String name;
int age;
// 2.在类内部创建好对象,私有化
private static Singleton s = new Singleton("张三", 1);
// 1. 私有化构造器
private Singleton (String name, int age) {
this.name = name;
this.age = age;
}
// 3.自定义方法,把类内创建好的对象送出去
public static Singleton getSingleton() {
return s;
}
public static void main(String[] args) {
Singleton s1 = Singleton.getSingleton();
System.out.println(s1.name + " " + s1.age);
Singleton s2 = Singleton.getSingleton();
System.out.println(s1);
System.out.println(s2);
/*
输出显示,s1和s2指向同一个对象,即“单例”。
*/
}
}
张三 1
Singleton@44e81672
Singleton@44e81672
继承
声明类时,使用extends
关键字表明要继承哪个类
class Person {
String name;
int age;
Person (String name, int age) {
this.name = name;
this.age = age;
}
}
class Worker extends Person {
}
理解继承:把多个类相同的部分再抽象成一个类,以后再遇到有类似的类,可以直接继承父类,不必再写重复的代码,而且若要修改,也只需修改父类一个类,不必再挨个修改各个子类。
继承的特点:
- 父类的私有方法和构造器不能被继承,因为私有方法只能在类内部被调用,构造器的名字必须和类名一致
- 子类可以调用父类中的属性和方法(非私有),创建子类的对象,会先调用父类的构造器,相当于在子类构造器第一行加
super(形参列表);
。 - 父类中不可以调用子类独有的成员
方法重写(Override)
方法重写的目的:父类中的方法,不适用于子类。
如何重写:
- 返回值类型、方法名、形参列表都与父类一样(JDK5开始可以返回子类类型)
- super.的方式调用父类的版本
- 访问权限不能比父类更小
- 不能抛出比父类更大的异常
包
如果只用类名作为区分类的标准是不可靠的。同事之间可能会定义重名的类,所以用package的概念进一步区分类,包也就是目录。
语法:
package 包名; // 创建单层包
package 包名1.包名2. …… .包名n; // 创建多层包
为了实现项目管理、解决命名冲突以及权限控制的效果。
包命名的原则:
org.apache.commons.lang.StringUtil
org.apache代表公司或组织信息;
commons代表项目名称
lang代表模块名称
包的导入:
import关键字:
import 类所在包名;
// 导入java目录中,lang目录中,System类中的静态成员out
import static java.lang.System.out;
final 关键字
final类:不能被继承。为了防止滥用继承
final方法:可以被继承,但是不能被重写。防止不经意间的重写。
final属性:必须被初始化,而且初始化后不能更改其值。
初始化属性:
- 声明时直接初始化
- 在代码块中初始化
- 在构造器中初始化
子类对象实例化的全过程
子类在实例化的过程中,一定会直接或间接地调用父类的构造器,然后其直接父类再去调用上面的构造器,直至到java.lang.Object类中的空构造。之所以子类可以使用父类中的属性和方法,也是因为在实例化子类的过程中,调用了所有父类的构造器。虽然创造子类对象的过程中调用了父类构造器,但是自始至终,只创造了一个对象,即为该子类对象。
Object类
包装类
多态
多态的目的:同一种事物表现出不同的形态。
多态语法:父类类型的引用指向子类类型的对象。
多态的使用场景:
- 直接声明父类类型的引用,指向子类类型的对象
- 实参是子类类型对象,形参是父类类型,形成多态
- 返回的值是子类类型,返回值类型要求的是父类类型
多态的特点:
- 父类类型引用指向子类对象,该引用可以直接调用父类和子类共有的方法,但不能直接调用子类独有的方法,需要强制类型转换位子类类型之后,方可调用。
- 对于普通的方法,编译时用父类的,实际运行时调用的是子类的;对于静态方法,编译和运行时都使用父类版本。
- 静态方法不能被重写。因为静态方法是类层级的,父类和子类不是同一个类,自然是两个不同的方法。
- 多态性指针对方法,对于属性,还是父类的
引用类型的类型转换:
- 引用类型的转换需要有父子类关系,否则抛出异常:ClassCastException
- 自动类型转换:小 -> 大
- 强制类型转换:大 -> 小
这里的大和小可以理解为范围。父类未经过修饰,可表示大范围,子类已经经过有了更多描述,表示小范围。所以子类转化为父类是自动的,反之需要强制转换。在转换之前要用instanceof关键字判断对象与类型的关系:
if (引用 instanceof 数据类型)判断引用是不是后面的类型。
父类中的方法被子类重写 ,此时父类中的这个方法相对于子类中被重写的那个方法来说,就是虚拟方法。使用多态的方法调用该方法时,在编译时调用的是父类中的虚拟方法,等到实际运行的时候调用的才是子类中被重写过的方法。可以说多态在调用方法时,是一种运行时行为。而与重写听起来差不多的重载,则是在编译期间就确定了具体方法的,属于编译时行为。
值得注意的是,重载,是可以发生在父类和子类之间的。
class Father {
public void show() {
System.out.println("你好!");
}
}
class Sun extends Father {
public void show(String somebody) {
System.out.println("你好!" + somebody);
}
}
public class DuoTai {
public static void main(String[] args) {
Sun s = new Sun();
s.show();
s.show("奥特曼");
}
}
你好!
你好!奥特曼
子类依然可以获取父类中的私有属性和方法,知识不能直接调用。子类可以通过父类中的可调用的方法去间接获取私有的属性和方法。
抽象类
抽象类大可以理解为普通类加上了抽象方法。继承了抽象类就一定得重回写抽象方法。
// 创建一个抽象类
abstract class Pet {
String name;
int age;
Pet() {}
Pet(String name, int age) {
this.name = name;
this.age = age;
}
// 普通方法
public void show() {
System.out.println(name + " " + age);
}
// 抽象方法
public abstract void bark();
}
class Cat extends Pet {
Cat(String name, int age) {
super(name, age);
}
@Override
public void bark(){
System.out.println("喵喵喵~?");
}
public static void main(String[] args) {
Cat c = new Cat("凯克",5);
c.bark();
}
}
喵喵喵~?
抽象方法:abstract关键字修饰、没有方法体。
抽象类:abstract关键字修饰、有抽象方法。其他成员也可正常拥有。可就是不许用来new对象,它的构造器是用来被继承的,抽象类的精髓也就在于被继承,是一种比普通类更有概括性的类。
抽象类被继承后其中的抽象方法必须被重写,除非子类还是抽象类。
抽象类的注意点:
- abstract与private修饰一个方法。(×)
private修饰的方法不能被继承,而abstract修饰的方法必须得被继承,故而报错。 - abstract与final修饰一个方法。(×)。
同理,final方法不能被继承 - abstract与static修饰一个方法。(×)
static方法理应可以用类名直接调用,可抽象方法不能做事 ,所以不可以。
接口
接口是比抽象类还抽象的类,但是不使用class
,换做interface
表达。
interface Animal{
public static final int EYES_NUMBER = 2;
public void eat();
public void run();
}
class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫猫吃饭!");
}
@Override
public void run() {
System.out.println("猫猫散步!");
}
public static void main(String[] args) {
Cat c = new Cat();
c.eat();
c.run();
}
}
猫猫吃饭!
猫猫散步!
接口中:
- 只允许有常量,public static final可以省略
- 不允许有普通方法,需全是抽象方法。java9开始允许有私有方法,为其他抽象方法所用。
- 不允许有构造方法
理解接口:接口类就像是一个插接件,实现类实现接口,就相当于是把接口提供的功能插到实现类上,属于锦上添花。Java支持实现多个接口,弥补了Java单继承的缺陷。
类与类 | extends | 单继承 |
---|---|---|
类与接口 | implements | 多实现 |
接口与接口 | extends | 多继承 |
接口只能继承接口,不能继承类。
从java8开始,接口中可以有default修饰的方法,重写与否全看自己需要。
interface Animal{
public static final int EYES_NUMBER = 2;
public void eat();
public void run();
public default void show() {
System.out.println("动物一般都有两只眼睛");
}
}
class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫猫吃饭!");
}
@Override
public void run() {
System.out.println("猫猫散步!");
}
public static void main(String[] args) {
Cat c = new Cat();
c.eat();
c.run();
c.show();
}
}
猫猫吃饭!
猫猫散步!
动物一般都有两只眼睛
内部类
定义
Java允许将一个类A声明到另一个类B的内部,则A是内部类,B是外部类。
内部类的分类
成员内部类:
- 静态
- 非静态
局部内部类:
- 方法内
- 代码块内
- 构造器内
Object类
java.lang.Object
- 新声明的类如果没有明确指明extends哪个类,则默认继承Object
- Object类是所有java类的根父类
class ObjectTest {
public static void main(String[] args) {
ObjectTest o = new ObjectTest();
System.out.println(o.getClass().getSuperclass());
}
}
class java.lang.Object
- Object类中的构造器只有无参构造器这一个
- Object类中的方法具有通用性。(Object类中没有属性)
Object类中的方法
- clone:创建并返回一对象的副本
- equals:比较两个对象是否相等
- toString:输出对象的字符串形式
- finalize:对象被销毁之前调用,用于垃圾回收
- getClass:获得类的类型
- hashCode:返回对象的哈希码值
- wait:导致当前线程等待它被唤醒
- notify、notifyAll:唤醒线程
重写equals方法
回顾 ==
面试题:==和equals的区别
:运算符,基本数据类型和引用数据类型都可以使用
如果比较的是基本数据类型,那么比较的是存储的值是否相等(不一定要求类型一样,能互相转换即可)。
如果比较的是引用数据类型,那么比较的是两个引用的地址值。
equals方法:
是一个方法,而非运算符。只能由对象调用,基本数据类型不能调用。Object类中的equals方法与作用相同。
像String、Date、File、包装类都重写了Obkect中的equals方法。重写以后比较的就是对象之间的“内容”是否相等。
重写equals
步骤:
- 比较两对象地址
- 比较两对象是否是同一个类型、参数对象是否为null
- 若是同类型,则类型转换,进行关键属性的实际内容的比较
重写toString方法
把想表达出来的信息通过字符串的方式返回
包装类(Wrapper)
IDEA中使用JUnit单元测试
感谢这位作者,鄙人亲测有效
要求:
测试类为public、此类应有public的无参构造器
测试方法为public、无形参列表、无返回值
测试方法前面加@Test注解,同时引入必要的包:import org.junit.Test;
重点:基本数据类型、包装类、字符串之间的互相转换
主要记住:自动装箱、自动拆箱、还有字符串和上面之间的转换即可
包装类常见面试题:
import org.junit.Test;
/**
* 关于包装类使用的面试题
*/
public class InterviewTest {
@Test
public void test1() {
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1); // 1.0
}
@Test
public void test2() {
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2); // 1
}
@Test
public void test3() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); // false
Integer m = 1;
Integer n = 1;
System.out.println(m == n); // true
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false
}
}
自动装箱池:Integer类内部定义了内部类IntegerCache,内部类中定义了缓存数组,存储了-128~127的整数。通过自动装箱技术创建出的Integer对象从缓存中直接取,而自己new的则不使用装箱池中的。
接口面试题:
// 第一题
interface A {
int x = 1; // 接口中的属性是常量
}
class B {
int x = 2;
}
public class C extends B {
public void show() {
//System.out.println(x); 编译不通过,因为x指向不明确
System.out.println(super.x); // 调用父类B中的
System.out.println(A.x); // 调用接口A中的
}
public static void main(String[] args) {
new C().show();
}
}
// 第二题
interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Roallable extends Playable, Bounceable {
Ball ball = new Ball("Pingpang");
}
public class Ball {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {
// ball = new Ball("Football"); ball是接口内的成员,是常量 有final修饰 不能再被赋值
// System.out.println(ball.getName);
}
}