java基础知识复习(面试重点)

javase复习重点

1.基本的数据类型

byte char short int long float double boolen

整型提升:的意义在于:表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的[通用寄存器]的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

强制类型转换:强制类型转换是把变量从一种类型转换为另一种数据类型。当 int 和 long 混合运算的时候, int 会提升成 long, 得到的结果仍然是 long 类型, 需要使用 long 类型的变量来 接收结果. 如果非要用 int 来接收结果, 就需要使用强制类型转换.

2.运算符

基本运算符 :+ - * / %(可以对double进行求模)

赋值运算符:+= -= *= /= %=

自增自减:++ –

关系运算符:== != < > <= >=

逻辑运算符:&& || !

&& 和 || 遵守短路求值的规则

位运算符:& | ~ ^ 位操作表示 按二进制位运算. 计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的 每一位依次进行计算。

移位运算符:<< >> >>> 无符号右移 >>>: 最右侧位不要了, 最左侧补 0

3.逻辑控制

顺序结构

分支结构:

if else语句

if(布尔表达式){
    //条件满足时执行代码
}else if(布尔表达式){
    //条件满足时执行代码
}else{
    //条件都不满足时执行代码
}

switch语句

switch(整数|枚举|字符|字符串){
 case 内容1 : {
 内容满足时执行语句;
 [break;]
 }
 case 内容2 : {
 内容满足时执行语句;
 [break;]
 }
 ...
 default:{
 内容都不满足时执行语句;
 [break;]
 } 
}

循环结构:

while循环

while(循环条件){ 
循环语句; 
} 

break:循环结束;

continue:跳过这次循环,进行下次循环

for循环

for(表达式1;表达式2;表达式3){ 
循环体; 
} 

猜数字游戏

import java.util.Random; 
import java.util.Scanner;; 
class Test { 
 public static void main(String[] args) { 
 Random random = new Random(); // 默认随机种子是系统时间
 Scanner sc = new Scanner(System.in); 
 int toGuess = random.nextInt(100); 
 // System.out.println("toGuess: " + toGuess); 
 while (true) { 
 System.out.println("请输入要输入的数字: (1-100)"); 
 int num = sc.nextInt(); 
 if (num < toGuess) { 
 System.out.println("低了"); 
 } else if (num > toGuess) { 
 System.out.println("高了"); 
 } else { 
 System.out.println("猜对了"); 
 break; 
 } 
 } 
 sc.close(); 
 } 
} 

4.方法的使用

// 方法定义
public static 方法返回值 方法名称([参数类型 形参 ...]){
 方法体代码;
 [return 返回值];
}
// 方法调用
返回值变量 = 方法名称(实参...);

基础类型来说, 形参相当于实参的拷贝. 即 传值调用 解决办法: 传引用类型参数 (例如数组来解决这个问题)

方法的重载(overload)

方法名相同

方法的参数不同(参数个数或者参数类型)

方法的返回值类型不影响重载.

当两个方法的名字相同, 参数也相同, 但是返回值不同的时候, 不构成重载.父子类中也可以发生重载。

在这里插入图片描述

方法的递归:

一个方法在执行过程中调用自身, 就称为 “递归”

5.数组的定义和使用

// 动态初始化
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
// 静态初始化
数据类型[] 数组名称 = { 初始化数据 };
  1. 使用 arr.length 能够获取到数组的长度. . 这个操作为成员访问操作符. 后面在面向对象中会经常用到.

  2. 使用 [ ] 按下标取数组元素. 需要注意, 下标从 0 开始计数

  3. 使用 [ ] 操作既能读取数据, 也能修改数据.

  4. 下标访问操作不能超出有效范围 [0, length - 1] , 如果超出有效范围, 会出现下标越界异常

在这里插入图片描述

存地址的变量就是引用 在打印String 类型的时候打印函数会进行转变。

1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.
\2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。)
\3. 堆:存放所有new出来的对象。
\4. 静态域:存放静态成员(static定义的)
\5. 常量池:存放字符串常量和基本类型常量(public static final)。有时,在嵌入式系统中,常量本身会和其他部分分割离开(由于版权等其他原因),所以在这种情况下,可以选择将其放在ROM中 。1.8之后

堆中
\6. 非RAM存储:硬盘等永久存储空间这里我们主要关心栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。

数组的拷贝方法

1.clone方法是从Object类继承过来的,基本数据类型(int ,boolean,char,byte,short,float ,double,long)都可以直接使用clone方法进行克隆,注意String类型是因为其值不可变所以才可以使用

int[] a1 = {1, 3};
int[] a2 = a1.clone();
String[] a1 = {"a1", "a2"};
String[] a2 = a1.clone();

2.System.arraycopy

public static native void arraycopy(Object src, int srcPos, Object dest, int desPos, int length)
    (原数组, 原数组的开始位置, 目标数组, 目标数组的开始位置, 拷贝个数)

3.Arrays.copyOf底层其实也是用的System.arraycopy

int[] a1 = {1, 2, 3, 4, 5};
int[] a2 = Arrays.copyOf(a1, 3);   //原数组,拷贝个数

4.Arrays.copyOfRange底层其实也是用的System.arraycopy,只不过封装了一个方法

int[] a1 = {1, 2, 3, 4, 5};
int[] a2 = Arrays.copyOfRange(a1, 0, 1);   //(原数组,开始位置,拷贝的个数)

6.类和对象

面向过程注重的是过程,在整个过程中所涉及的行为,就是功能。

面向对象注重的是对象,也就是参与过程所涉及到的主体。是通过逻辑将一个个功能实现连接起来

类用class进行修饰 对象就是对类的实例化

class Person {
    public int age;//成员属性 实例变量
    public String name;
    public String sex;
    public void eat() {//成员方法
       System.out.println("吃饭!");  
   }
    public void sleep() {
       System.out.println("睡觉!");  
   }
}
public class Main{
 public static void main(String[] args) {
        Person person = new Person();//通过new实例化对象
        person.eat();//成员方法调用需要通过对象的引用调用
        person.sleep();
        //产生对象     实例化对象
        Person person2 = new Person();
        Person person3 = new Person();
 }
}

静态成员方法 (变量) 普通成员方法(变量)

对于一个对象的字段如果没有显式设置初始值, 那么会被设置一个默认的初值

null 在 Java 中为 “空引用”, 表示不引用任何对象. 类似于 C 语言中的空指针. 如果对 null 进行 . 操作就会引发异常.

普通成员变量 存在堆上 普通方法(栈上)(堆上有方法表通过方发表在方法区寻找方法)

静态方法存在方法区 (在调用的时候通过类名.) 静态成员变量(类变量 不依赖对象 存在方法区)

final修饰 的是常量 要大写存在堆

打印引用的时候需要重写tostring方法

7.封装

oop(面向对象编程)语言特征:继承 封装 多态

封装:这样的代码导致类的使用者(main方法的代码)必须要了解 Person 类内部的实现, 才能够使用这个类. 学习成本较 高 一旦类的实现者修改了代码(例如把 name 改成 myName), 那么类的使用者就需要大规模的修改自己的代码, 维 护成本较高。

private 不光能修饰字段, 也能修饰方法 通常情况下我们会把字段设为 private 属性, 但是方法是否需要设为 public, 就需要视具体情形而定. 一般我们希 望一个类只提供 “必要的” public 方法, 而不应该是把所有的方法都无脑设为 public.

需要获取或者修改这个 private 属性, 就需要使用 getter / setter 方法(alt+ f12)

class Person { 
 private String name;//实例成员变量
 private int age; 
 
 public void setName(String name){ 
 //name = name;//不能这样写
 this.name = name;//this引用,表示调用该方法的对象
 } 
 public String getName(){ 
 return name; 
 } 
 
 public void show(){ 
 System.out.println("name: "+name+" age: "+age); 
 } 
} 
public static void main(String[] args) { 
 Person person = new Person(); 
 person.setName("caocao"); 
 String name = person.getName(); 
 System.out.println(name); 
 person.show(); 
} 

静态方法内部不可以使用this

私有的方法只能在类里面进行使用

this.data this.func() 代表当前对象 调用的是成员变量和成员方法 this()调用构造方法

构造方法:没有返回值的一个方法,方法名和类名一样(用来实例化对象(每个类会有一个不带参数的构造方法))

class Person { 
 
 private String name;//实例成员变量
 private int age; 
 private String sex; 
 //默认构造函数 构造对象 
 public Person() { 
 this.name = "caocao"; 
 this.age = 10; 
 this.sex = "男"; 
 } 
 //带有3个参数的构造函数
 public Person(String name,int age,String sex) { 
 this.name = name;    //代表当前对象的引用
 this.age = age; 
 this.sex = sex; 
}
}

构造方法参数没有限制,方法之间可以进行重载

代码块:也可以对对象进行实例化

{ }定义一段代码块

普通(本地代码块)方法块:定义在方法中

实例代码块:(构造代码块)不加修饰 //实例得是数据成员

{ 
 this.name = "bit"; 
 this.age = 12; 
 this.sex = "man"; 
 System.out.println("I am instance init()!"); 
 } 

静态代码块 只能初始化静态的 (只执行一次)

static { 
 count = 10;//只能访问静态数据成员 
 System.out.println("I am static init()!"); 
 } 

先执行静态代码块 实例代码块 构造方法

匿名对象(只使用一次,传参的时候)

8.复杂度

时间复杂度和空间复杂度

衡量算法执行的时间效率 衡量算法的空间的效率

O(1) O(log2N) O(n) O(nlog2N) O(n^2)

9.顺序表和链表

顺序表:底层是一个数组

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import org.w3c.dom.ls.LSOutput;

import java.util.Arrays;

/**
 * Created with Intellij IDEA
 * Description:
 * User: 23871
 * Date: 2021-04-10
 * Time: 17:26
 */
public class MyArray {
    public  int[] elem;
    public  int usedSize;
    public MyArray() {
        this.elem =new int[10];
    }
    //在pos位置新增元素
    public  void add(int pos,int data){
        //0.判断满了没
        if(usedSize==elem.length){
            System.out.println("数组已满,无法插入");

            return;
        }
        //判断合法性
        else if(pos<0||pos>usedSize){
            System.out.println("坐标不合法");
            return;
        }
        else if(pos==usedSize){
            elem[pos]=data;
            usedSize++;
            return;
        }
        //从后往前挪
        else{
            for(int i=usedSize;i>=pos;i--){
                elem[i+1]=elem[i];
            }
            elem[pos]=data;
            usedSize++;
            return;
        }
    }
    //打印顺序表
    public void display(){
        for (int i = 0; i <usedSize ; i++) {
            System.out.print(elem[i]+" ");
        }
        System.out.println();
    }
    //判断顺序表是否满了
    public boolean isFull() {
        if(this.usedSize == this.elem.length) {
            return true;

        }
        return false;
    }
    //判定是否包含某个元素
    public  boolean contains (int toFind ){
        for (int i = 0; i <usedSize ; i++) {
            if(elem[i]==toFind)
            return true;
        }
       return false;
    }
    //查找某个元素对应的位置
    public int  search(int toFind) {
        if(contains(toFind)==true){
        int i = 0;
            for (; i <usedSize ; i++) {
                if (elem[i] == toFind)
                return i;
            }
        }
        return -1;
    }
        //获取pos位置的元素
    public  int getPos(int pos){
        //判断pos是否合法
        if(pos<0||pos>=this.usedSize) {
            return -1;
        }
        return this.elem[pos];
    }
    //给pos位置的元素设置为value
    public void setPos(int pos,int value){
        if(pos<0||pos>=this.usedSize) {
            System.out.println("pos不合法");
        }else{
            elem[pos]=value;
        }
    }
    //删除第一次出现的关键字
    public  void remove(int toRemove){
         int index=search(toRemove) ;
         if(index==-1){
             System.out.println("你要删的不存在");
         }
        int i=index;
        for (; i <this.usedSize-1 ; i++) {
            elem[i]=elem[i+1];
            }
        this.usedSize--;
        }
  //获取顺序表大小
    public  int size(){
     return this.usedSize;
    }
    //清空顺序表
    public void clear(){
        usedSize=0;
        System.out.println("顺序表已经清空!");
    }
}

顺序表(物理上逻辑上都是连续的)的不足:

增加和删除的时候时间复杂度是O(n) 扩容时候会浪费空间,

好处:

便于查找,

链表(物理上不一定联系,逻辑上一定连续)

单链表 单向不带头节点 非循环

/**
 * Created with Intellij IDEA
 * Description:
 * User: 23871
 * Date: 2021-04-12
 * Time: 20:42
 */
class Node{
    public int val;
    public Node next;  //null

    public Node(int val) {
        this.val = val;
    }
}
public class MyLinkedList {
    public Node head=null;

    //穷举的方法创建单链表
   /* public void creatlist(){
        Node node1=new Node(1);
        Node node2=new Node(2);
        Node node3=new Node(3);
        Node node4=new Node(4);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        head.next=node1;

    }
*/

    //头插法
    public void addFirst(int val){
        Node node=new Node(val);
        node.next=this.head;
        this.head=node;

    }
    //尾插法
    public  void addLast(int val){
        Node node =new Node(val);

        if(this.head==null){
            this.head=node;
        }else {
            Node cur =this.head;
            while (cur.next!= null) {
                cur=cur.next;
            }
            cur.next=node;
        }

    }
    //在固定位置插入
    public  void addIndex(int index,int data){
        if(index<0||index>getLength()){
            System.out.println("位置不合法");
            return;
        }if(index==0){
            addFirst(data);
            return;
        }if(index==getLength()){
            addLast(data);
            return;
        }else{
            Node ret=searchSub(index);
            Node node=new Node(data);
            node.next=ret.next;
            ret.next=node;

            }
    }

    //寻找给定位置的Node
    public  Node searchSub(int index){
        Node cur=this.head;
        while(index-1!=0){
            cur=cur.next;
            index--;
        }
        return cur;
    }
    //获取链表长度
    public int getLength(){
        Node cur=this.head;
        int count=0;
        while (cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }
    //判断是否存在关键字key在单链表中
    public  boolean contains(int key){
        Node cur=this.head;
        while(cur!=null){
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }
    //删除第一次出现关键字为key的节点
    public  void remove(int data){
        if(head.val==data){
            this.head=this.head.next;
        }
        Node ret = searchPost(data);
        if(ret==null){
            System.out.println("不存在该关键字");
        }else {

            ret.next = ret.next.next;
        }
    }
    //寻找给定数值的位置
    public Node searchPost(int data){
        Node cur=this.head;
        while(cur.next!=null){
            if(cur.next.val==data){
               return cur;
            }
            cur=cur.next;
        }
        return null;
    }
    //删除所有值为key的
    public  void removeAllkey(int key){
        Node prev=head;
        Node cur=head.next;
        while(cur!=null){
            if(cur.val==key){
              prev.next=cur.next;
              cur=cur.next;
            }
           else {
               prev=cur;
               cur=cur.next;
            }
        }
        if(this.head.val==key){
            head=head.next;
        }
    }
    //清空 链表
    public  void clear(){
        Node cur=head;

        while(cur!=null){
            Node curNext=cur.next;
            cur.next=null;
            cur=curNext;
        }
    }
    //打印单链表
    public void show(){
        Node cur =this.head;
        while(cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }

    }
    //链表的中间值
    public Node middleNode(){
        Node slow=head;
        Node fast=head;
        while(fast!=null&&fast.next!=null) {
            fast = fast.next.next;
            slow = slow.next;

        }
        return  slow;
    }
    //链表的反转
    public  Node trunOver(){
        if(head==null||head.next==null){
            return head;
        }
        Node cur=this.head;
        Node prve=cur.next;
        cur.next=null;
        cur=prve;
        while(cur!=null){
            prve=cur.next;
            cur.next=head;
            head=cur;
            cur=prve;
        }
        return  head;
    }

}

双向链表

import test.src.Mylist;
import static sun.plugin2.os.windows.OVERLAPPED.size;
class ListNode{
    public int val;
    public  ListNode prev;
    public ListNode next;  //null

    public ListNode(int val) {
        this.val = val;
    }
}
public class MyList {
    public ListNode head;
    public ListNode tail;
    public MyList(){
        ListNode node=new ListNode(-1);
        this.head=node;
        this.tail=node;

    }


    //头插法
    public void addFirst(int val) {
        ListNode node=new ListNode(val);
        if(head==null){
            head=node;
            tail=node;
        }else {
            node.next=head;
            head.prev=node;
            head=node;
        }
    }
    public void display(){
       ListNode cur =head;
       while(cur!=null){
           System.out.print(cur.val+" ");
           cur=cur.next;
       }

    }
    //尾插法
    public void addLast(int data){
        ListNode node=new ListNode(data);
        if(tail==null){
            head=node;
            tail=node;
        }else {
            tail.next=node;
            node.prev=tail;
            tail=node;
        }

    }
     //任意位置插入,第一个数据节点为0号下标
    public boolean addIndex(int index, int data){
        if (index < 0 || index > size()){
            System.out.println("位置不合法");
            return false;
        }
        if (index == 0) {
            addFirst(data);
            return true;
        }
        if (index == size()) {
            addLast(data);
            return true;
        }
        else{
            ListNode ret=findIndex(index);
            ListNode node = new ListNode(data);
            node.next = ret.next;
            node.prev=ret;
            ret.next = node;
            ret.next.prev=node;
        }
        return true;
    }
    public  ListNode findIndex(int index){
        ListNode cur=head;
        while(index-1!=0){
            cur=cur.next;
            index--;
        }
        return  cur;
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                return  true;
            }
            cur=cur.next;
        }
       return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                //删除头结点
                if(this.head.val==key){
                    this.head=head.next;
                    if(this.head!=null){
                        this.head.prev=null;
                        this.tail=null;
                    }

                }else {
                    //中间和尾巴
                    if(cur.next!=null){
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }else {
                        cur.prev.next = cur.next;
                        tail = cur.prev;
                    }
                }
                return;
                }else {
                        cur=cur.next;
                }
            }

    }
    //删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key) {
                //删除头结点
                if (this.head.val == key) {
                    this.head = head.next;
                    if (this.head != null) {
                        this.head.prev = null;
                        this.tail = null;
                    }
                } else {
                    //中间和尾巴
                    if (cur.next != null) {
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    } else {
                        cur.prev.next = cur.next;
                        tail = cur.prev;
                    }
                }
            }
                cur=cur.next;
        }
    }
    //得到单链表的长度
    public int size(){
            ListNode cur = this.head;
            int count = 0;
            while (cur != null) {
                count++;
                cur = cur.next;
            }
            return count;
    }
    public void clear(){
        ListNode cur=head;
        while(cur!=null){
            ListNode curNext=cur.next;
            cur.prev=null;
            cur.next=null;
            cur=cur.next;
        }
        head=null;
        tail=null;
    }
}

数组和链表之间的区别(顺序表和链表的区别)ArrayList和linklist区别 逻辑上连续 链表物理上不连续

链表:增 尾插0(n)头插O(1)不需要移动元素

顺序表 需要挪动元素 最后一个不需要 满了还要扩容

链表 :删除 O(1) O(n) 只需要修改指向

顺序表:移动元素

链表:查 改 O(n)

顺序表:给下标0(1)从头找O(n)

面向对象编程

包:使用包的主要目的是保证类的唯一性 使用 import进行导包 util.* 用什么到什么

常用的包

  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。

  2. java.lang.reflect:java 反射编程包;

  3. java.net:进行网络编程开发包。

  4. java.sql:进行数据库开发的支持包。

  5. java.util:是java提供的工具程序包。(集合类等) 非常重要

  6. java.io:I/O编程开发包。

包的控制权限

在这里插入图片描述

private: 类内部能访问, 类外部不能访问

默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.

protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.

public : 类内部和类的调用者都能访问

继承

代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法). 有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联.

减少代码的冗余(例如 animal cat bird)

这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的. 这三个类都具备一个相同的 name 属性, 而且意义是完全一样的. 从逻辑上讲, Cat 和 Bird 都是一种 Animal

语法规则

class Animal { 
 public String name; 
 public Animal(String name) { 
 this.name = name; 
 } 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
class Cat extends Animal { 
 public Cat(String name) { 
 // 使用 super 调用父类的构造方法. 
 super(name); 
 } 
} 

Cat 子类 派生类 Animal 基类 父类 超类 继承的好处:代码的复用

继承的时候不会继承构造方法 其他都被继承

在构造子类的时候先帮助父类进行构造 调用合适的构造方法

super: super()调用父类的构造方法 super.data 调用父类的成员属性 super.function()调用父类的成员方法

final: 修饰常量 final class A{}密封类 不能继承 final 修饰方法 密封方法

组合(一部分 包含的关系)

多态(shape 三角形 圆形 矩形)

向上转型: 子类对象给父类 Animal animal=new Dog(); 父类的引用引用了子类的对象

向上转型的时机 直接赋值 传值 返回值

向下转型:父类对象给子类对象 (先要向上转型)

判断是否进行了向上转型(父类的引用 instanceof 子类)谁是谁的实例

动态绑定:发生了向上转型 父类和子类有同名的重写、覆盖 复写的方法 父类的引用调用子类的同名覆盖方法

重写:方法名称相同 参数列表个数类型相同 返回值也要相同(final修饰的方法不可以重写)

注意:静态的方法不能被重写

private default protected public 子类如果重写父类的方法必须要权限大于等于父类

private修饰的父类方法是不可以被重写的

构造方法中也会发生动态绑定

多态:

类调用者对类的使用成本进一步降低. 封装是让类的调用者不需要知道类的实现细节. 多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可. 因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低

能够降低代码的 “圈复杂度”, 避免使用大量的 if - else

可扩展能力更强

抽象类 接口

abstract class Shape { 
  abstract public void draw(); 
} 
1、抽象类可以被继承,也可以发生向上转型,动态绑定
2、抽象类不可以被实例化
3、抽象类当中的方法一定要被子类重写
4、抽象类存在的意义就是为了被继承-》因为:你就根本不能实例化。
5、如果是抽象类继承了一个抽象类,那么可以不重写这个抽象方法。但是,如果这个抽象类再次被一个普通的类继承,呢么一定要重写的。
6.抽象方法不能是private
7.可以包含普通方法
8.抽象类不能被final修饰
interface IShape { 
 void draw(); 
} 
接口:使用关键字interface来修饰的。
1、接口当中的方法默认是public abstract不能有具体的实现
2、接口不能实例化
3、接口当中的方法默认是public static final
4、类和接口直接的关系是implements 此时接口当中的所有方法,都要被重写。(实现)
5、也可以发生向上转型-》运行时绑定(动态绑定)
6、JDK1.8开始,接口当中的方法可以有具体的实现,但是这个方法一定要被default修饰。
7、在Java当中一个类可以实现多个接口
8、类和接口是implements接口和接口之间是extends  (扩展)

在这里插入图片描述

自己写的类型进行排序需要重写comparable。comparator比较器

comparable 一旦类写死就无法更改

comparator 比较器 比较方便 想使用什么比较实现什么比较器

克隆的时候 Cloneable 空接口 表示接口 重写 Object的 clone方法

String类

实例化 存放在堆上 的常量池中

String str=“he”+“llo”

String str1=“hello”

str==str1 true 常量

字符串常量池:只存放一份 使用的时候取出他的地址, 如果没有就会添加, 节省资源

// 该字符串常量并没有保存在对象池之中
String str1 = new String("hello") ; 
String str2 = "hello" ; 
System.out.println(str1 == str2); 
// 执行结果
false
    
String str1 = new String("hello").intern() ; 
String str2 = "hello" ; 
System.out.println(str1 == str2); 
// 执行结果
true

1、每次拼接会产生1个新的对象不会再原有的基础上进行修改
2、value的指向不能发生改变。

String的拼接会被优化为 Stringbuilder StringBudder

string的拼接会产生新的对象 但是stringbuilder和stringbuffer的拼接会使用append 返回当前对象

朴素算法 kmp算法

  1. equals 2. compareTo 3. toCharArray 4. contains 5. indexOf 6. lastIndexOf 7. replaceAll 8. replaceFirst 9. split 10. subString 11. trim 12. isEmpty 13. length

异常

运行时异常(非受查异常)

编译时异常(受查异常)

处理异常

try{ 
 有可能出现异常的语句 ; 
}[catch (异常类型 异常对象) {
} ... ]
[finally {
 异常的出口
}]

try 代码块中放的是可能出现异常的代码. catch 代码块中放的是出现异常后的处理行为. finally 代码块中的代码用于处理善后工作, 会在最后执行. 其中 catch 和 finally 都可以根据情况选择加或者不加

程序先执行 try 中的代码

如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.

如果找到匹配的异常类型, 就会执行 catch 中的代码

如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.

无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).

如果上层调用者也没有处理的了异常, 就继续向上传递.

一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止
在这里插入图片描述

手动抛出异常

public static int divide(int x, int y) throws ArithmeticException { 
 if (y == 0) { 
 throw new ArithmeticException("抛出除 0 异常"); 
 } 
 return x / y; 
} 

自定义异常 必须要继承 Exception 或者 RuntimeException

class UserError extends Exception { 
 public UserError(String message) { 
 super(message); 
 } 
} 
public static void main(String[] args) { 
 try { 
 login("admin", "123456"); 
 } catch (UserError userError) { 
 userError.printStackTrace(); 
 } 
} 
public static void login(String userName, String password) throws UserError, { 
 if (!Test.userName.equals(userName)) { 
 throw new UserError("用户名错误"); 
 } 
System.out.println("登陆成功"); 
} 

Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现,compareTo方法也被称为自然比较方法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:

1、比较者大于被比较者(也就是compareTo方法里面的对象),那么返回正整数
2、比较者等于被比较者,那么返回0
3、比较者小于被比较者,那么返回负整数

二、Comparator可以认为是是一个外比较器,个人认为有两种情况可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式

Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
1、o1大于o2,返回正整数
2、o1等于o2,返回0
3、o1小于o3,返回负整数

总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:
1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法
2、实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,则需要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。
在这里插入图片描述

深拷贝:拷贝完成后修改拷贝后的新数组的元素不影响原来数组的元素就叫深拷贝。
浅拷贝:修改元素数据后也会影响原数组元素的数据

1. 浅拷贝介绍

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

浅拷贝

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

类实现Cloneable接口 复写clone()方法

深拷贝

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。

compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
1、o1大于o2,返回正整数
2、o1等于o2,返回0
3、o1小于o3,返回负整数

总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:
1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法
2、实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,则需要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。
[外链图片转存中…(img-ezBmXbDj-1628770965772)]

深拷贝:拷贝完成后修改拷贝后的新数组的元素不影响原来数组的元素就叫深拷贝。
浅拷贝:修改元素数据后也会影响原数组元素的数据

1. 浅拷贝介绍

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

浅拷贝

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

类实现Cloneable接口 复写clone()方法

深拷贝

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable并重写 clone()` 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

力争做大牛的小王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值