Java易错知识点整理
栈和堆
新建一个对象——放在栈里 int[] array;
堆,内存里———— array =new int[10];
关于java抽象override
类无法访问父类的private
字段或者private
方法。protected
的变量或者方法可以访问
子类必须重写父类的构造方法
子类必须重写抽象类的抽象方法
在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface
抽象类和接口的对比如下:
abstract class | interface | |
---|---|---|
继承 | 只能extends一个class | 可以implements多个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 |
super
super
关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName
。例如:public class Main {
public static void main(String[] args) {
Student s = new Student("Xiao Ming", 12, 89);
}
}
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
this.score = score;
}
}
Main.java:21: error: constructor Person in class Person cannot be applied to given types;
public Student(String name, int age, int score) {
^
required: String,int
found: no arguments
reason: actual and formal argument lists differ in length
1 error
error: compilation failed
这是因为在Java中,任何class
的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();
,所以,Student
类的构造方法实际上是这样:
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(); // 自动调用父类的构造方法
this.score = score;
}
}
因此我们得出结论:如果父类没有默认的构造方法,子类就必须显式调用super()
并给出参数以便让编译器定位到父类的一个合适的构造方法。
这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
抽象类
如果一个class
定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract
修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
使用abstract
修饰的类就是抽象类。我们无法实例化一个抽象类:
Person p = new Person(); // 编译错误
无法实例化的抽象类有什么用?
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
例如,Person
类定义了抽象方法run()
,那么,在实现子类Student
的时候,就必须覆写run()
方法:
1.构造方法与构造器
有两种类型的构造函数
-
默认构造函数(无参数构造函数)
public People( ){
}
-
参数化构造函数
public People(String name){
}
一、this关键字主要有三个应用:
(1)this调用本类中的属性,也就是类中的成员变量;
(2)this调用本类中的其他方法;
(3)this调用本类中的其他构造方法,调用时要放在构造方法的首行。
Public Class Student {
String name; //定义一个成员变量name
private void SetName(String name) { //定义一个参数(局部变量)name
this.name=name; //将局部变量的值传递给成员变量
}
}
继承
提高了代码的复用性
提高了代码的维护性
变量的访问特点:子类访问变量的顺序
1:在子类方法内部
2.子类的的成员变量
3.再去父类的成员变量
4.如果都没有,就报错,不考虑父类的父类
super和this的区别
父类空间优先于子类对象产生 在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空 间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构 造方法调用时,一定先调用父类的构造方法。理解图解如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dgY8lMhM-1617268870562)(Java易错知识点整理.assets/image-20210320113738753.png)]
super和this的含义
super :代表父类的存储空间标识(可以理解为父亲的引用)。
this :代表当前对象的引用(谁调用就代表谁)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zkeM593c-1617268870565)(Java易错知识点整理.assets/image-20210320103047487.png)]
this.成员变量 ‐‐ 本类的
super.成员变量 ‐‐ 父类的
this.成员方法名() ‐‐ 本类的
super.成员方法名() ‐‐ 父类的
-
访问构造方法
this(...) ‐‐ 本类的构造方法 super(...) ‐‐ 父类的构造方法
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。 super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
方法重写
成员方法重名——重写(Override)
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
重写的应用
子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。
class Phone {
public void sendMessage(){
System.out.println("发短信");
}
public void call(){
System.out.println("打电话");
}
public void showNum(){
System.out.println("来电显示号码");
}
}
/* 小贴士:这里重写时,用到super.父类成员方法,表示调用父类的成员方法。
注意事项
1. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
2. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
1.5 继承后的特点——构造方法
当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先我们要回忆两个事情,构造方法的定义格式和作用。
1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
2. 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构
造方法中默认有一个
super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。代
码如下:*/
//智能手机类
class NewPhone extends Phone {
//重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
//调用父类已经存在的功能使用super ---------调用父类的方法
super.showNum();
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}
public class ExtendsDemo06 {
public static void main(String[] args) {
// 创建子类对象
NewPhone np = new NewPhone();
// 调用父类继承而来的方法
np.call();
// 调用子类重写的方法
np.showNum();
}
}
注意事项
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。 (public>默认>private)
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
权限修饰符
public:公共的。
protected:受保护的
default:默认的
private:私有的
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
成员变量使用
private
,隐藏细节。构造方法使用
public
,方便创建对象。成员方法使用
public
,方便调用方法。
小贴士:不加权限修饰符,其访问能力与default
修饰符相同
final关键字
学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承 API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了 final 关键字, 用于修饰不可改变内容。
final: 不可改变。可以用于修饰类、方法和变量。
类:被修饰的类,不能被继承。
方法:被修饰的方法,不能被重写。
变量:被修饰的变量,不能被重新赋值。
Static关键字
非静态的成员方法
能访问静态成员变量
能访问非静态成员变量
能访问静态成员方法
能访问非静态成员方法
静态的成员方法
能访问静态成员变量
能访问静态成员方法
接口Interface
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
含有抽象方法
抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
public interface InterFaceName {
public abstract void method();
void method();
}
含有默认方法和静态方法
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。 静态方法:使用 static 修饰,供接口直接调用。基本的实现
public interface InterFaceName {
public default void method() {
// 执行语句
}
public static void method2() {
// 执行语句
}
}
基本的实现
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。
实现的动作类 似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。
非抽象子类实现接口:
- 必须重写接口中所有抽象方法。
- 继承了接口的默认方法,即可以直接调用,也可以重写。
迭代器Iterator
Iterator的定义如下:
public interface Iterator<E> {}
default void forEachRemaining(Consumer<? super E> action)
执行给定的每个剩余元素的动作,直到所有的元素都被处理或操作抛出异常。
boolean hasNext() //判断是否有元素没有被遍历
返回 true如果迭代具有更多的元素。
E next()
返回迭代中的下一个元素。
default void remove()
从基础集合中移除这个迭代器返回的最后一个元素(可选操作)。
实现:
// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;
public class RunoobTest {
public static void main(String[] args) {
// 创建集合
ArrayList<String> sites = new ArrayList<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
// 获取迭代器
Iterator<String> it = sites.iterator();
// 输出集合中的所有元素,循环输出
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
https://www.runoob.com/java/java-iterator.html
增强for循环
JAVA中的增强for循环底层是通过迭代器模式来实现的。
//定义格式
for(变量类型 变量:需迭代的数组或集合){
}
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + ",");
}
for (Integer i : list) {
System.out.print(i + ",");
}
Hash
文档:
hashCode
public int hashCode()
返回一个对象的哈希代码值。这种方法对于hash表,如所提供的 HashMap利益支持。
对hashCode一般合同:
如果根据equals(Object)法两个对象是相等的,那么调用hashCode方法每一个对象必须产生相同的整数结果。
它不是必需的,如果按照equals(java.lang.Object)法两个对象是不平等的,然后调用hashCode方法每一个对象必须产生不同的整数结果。然而,程序员应该意识到,产生不同的整数结果的不平等的对象可能会提高哈希表的性能。
尽合理的切实可行,由类Object定义hashCode方法返回不同的对象不同的整数。(这通常是通过将该对象的内部地址转换成一个整数,但这不是实现实现技术的™java编程语言。要求)
结果
此对象的哈希代码值。
另请参见:
equals(java.lang.Object), System.identityHashCode(java.lang.Object)
冒泡排序
import java.util.Arrays;
//排序代码
public class Bubble {
/*
对数组a中的元素进行排序
*/
public static void sort(Comparable[] a) {
/*
i
4 8 6 7 5 3
0 1 2 3 4 5 length=6
j j
*/
for (int i = a.length - 1; i > 0; i--) { //for(int i=0;arr<arr.length-1;i++)
for (int j = 0; j < i; j++) { // for(int j=i+1;j<arr.length,j++)
if (greater(a[j], a[j + 1])) { if(greater(arr[i],arr(j)))
exch(a, j, j + 1);
}
}
}
}
/*
比较v元素是否大于w元素
*/
private static boolean greater(Comparable v, Comparable w) {
return v.compareTo(w) > 0;
}
/*
数组元素i和j交换位置
*/
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
public static void main(String[] args) {
Integer[] a = {4, 5, 6, 3, 2, 1};
// Bubble bubble =new Bubble();
bubble.sort(a);
Bubble.sort(a);
System.out.println(Arrays.toString(a));
}
}