Java基础知识总结

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 指定源文件的版本
*/

Javadoc操作方式

Java基本语法

关键字、标识符
关键字

被Java赋予特殊意义的的字符串。

标识符

凡是可以自己定义名字的地方都是标识符。
标识符的定义规则:

  1. 数字、字母、下划线、$,数字不能打头
  2. 不可以使用关键字和保留字(保留字:Java现在并未使用,但是不可作为标识符。)
  3. Java严格区分大小写,长度无限制
  4. 支持中文,不能有空格,要做到见名知意

标识符命名规范:

  1. 包名:多个单词组成时,每个单词都小写
  2. 类名、接口名:每个单词首字母大写
  3. 变量名、方法名:第一个单词首字母小写,其余单词首字母大写
  4. 常量名:单词所有字母大写,单词与单词之间用“_”隔开

变量

变量的种类、每一种变量的特点、需要注意的几个小点、定义和使用变量要遵循的规则

Java数字据类型

整型

byte:1字节,-128~127
short:2字节,-32768~32767
int:4字节,-231~231-1(约21亿)
long:8字节,-263~263-1
注意:

  • 声明long类型时,末尾加lL
  • 用常量初始化整型时,默认该常量是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字节,只有truefalse
注意:

  • 实打实的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. 算术运算符:加、减、乘、除、取余、正负号、++、–、字符串连接
    取余的结果符号和被除数一样;前++是先使用值作为表达式的值,再+1,–同理;字符串连接取决于+左右的运算成员有没有字符串。
  2. 赋值运算符:=、+=、-=、*=、/=…
    赋值运算符(包括++、–,不包括=)不会改变变量的数据类型
  3. 比较运算符:> >= < <= == != instanceof
  4. 逻辑运算符:& && | || ! ^(逻辑与、短路与、逻辑或、短路或、非、异或)
    短路运算:逻辑运算符左边的表达式已经足以判断出整个表达式的结果时,则不再进行右侧表达式的计算
  5. 位运算符:& | ~ ^ << >> >>>(与、或、非、异或、左移、右移、无符号右移)
    左移:右边的空位补0,右移:左边的空位补符号位的值,无符号右移:左边空位补0
  6. 三元运算符:(条件表达式) ? (表达式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,此时不可以直接访问元素值。
数组常见算法(概述)

数据结构

  1. 数据结构指的是数据与数据之间的逻辑关系:集合、一对一、一对多、多对多
  2. 数据的存储结构:
    线性表:顺序表(比如数组),链表、栈、队列
    树形结构:二叉树
    图形结构: … …

算法

  1. 排序算法
  2. 搜索算法
  • 针对数值型数组
    最大值、最小值、平均数、总和

  • 数组的赋值与复制

  • 数组元素反转

数组中指定元素查找:

  • 线性查找
  • 二分查找

面向对象部分

类和对象

什么是类、对象

类是一类事物的抽象、总结,好比是生产零件的图纸;对象是具体的某一个事物,就像是生产出来的具体的某一个零件。
类中的成员:

  • 属性
  • 方法
  • 构造方法
  • 代码块
  • 内部类
    接下来

类的加载:执行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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

封装

封装的目的:维持代码的健壮性,并且掩盖代码的实现细节,让注意力集中在业务逻辑上。
实现步骤:

  1. 私有化属性
  2. 提供共有的get、set方法操作属性,并进行合理值得判断
  3. 在构造器中使用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

  1. 新声明的类如果没有明确指明extends哪个类,则默认继承Object
  2. Object类是所有java类的根父类
class ObjectTest {
	public static void main(String[] args) {
		ObjectTest o = new ObjectTest();
		System.out.println(o.getClass().getSuperclass());
	}
}

class java.lang.Object

  1. Object类中的构造器只有无参构造器这一个
  2. Object类中的方法具有通用性。(Object类中没有属性)
Object类中的方法
  • clone:创建并返回一对象的副本
  • equals:比较两个对象是否相等
  • toString:输出对象的字符串形式
  • finalize:对象被销毁之前调用,用于垃圾回收
  • getClass:获得类的类型
  • hashCode:返回对象的哈希码值
  • wait:导致当前线程等待它被唤醒
  • notify、notifyAll:唤醒线程
重写equals方法
回顾 ==

面试题:==和equals的区别
:运算符,基本数据类型和引用数据类型都可以使用
如果比较的是基本数据类型,那么比较的是存储的值是否相等(不一定要求类型一样,能互相转换即可)。
如果比较的是引用数据类型,那么比较的是两个引用的地址值。
equals方法:
是一个方法,而非运算符。只能由对象调用,基本数据类型不能调用。Object类中的equals方法与
作用相同。
像String、Date、File、包装类都重写了Obkect中的equals方法。重写以后比较的就是对象之间的“内容”是否相等。

重写equals

步骤:

  1. 比较两对象地址
  2. 比较两对象是否是同一个类型、参数对象是否为null
  3. 若是同类型,则类型转换,进行关键属性的实际内容的比较

重写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);
    }
}

异常

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值