day13 面向对象编程

1、链表

遍历太慢,所以加计数器

链表有子链表:递归遍历

不过一般还是用循环,比较快

改进版

package com.atguigu.javase.HomeWork;


class Node{

    Object value;//值域
    Node next;//指针域
    //提供一个构造器
    public Node(Object value){
        this.value = value;
    }

}
class Link{

    private Node head;//头节点 指向第一个元素
    private Node tail;//尾结点 指向最后一个元素
    int size = 0;

    //添加结点的方法
    public void add(Object val){
        Node newNode = new Node(val);//创建Node将值传入
        if(head == null){//如果头节点为空,那么就将第一个结点作为头结点和尾结点、
            head = newNode;
            tail = newNode;
        }else{//头部不为空,在尾部链入新结点  需要修改两个地方
            tail.next = newNode;//1、尾结点指向结点的指针域 指向newNode结点
            tail = newNode;//2、尾结点本身也要指向新结点的地址
        }
        size++;
    }
    //遍历单链表 需要一个小引用
    public void travel(){
        Node tmp = head;//让这个引用指向头结点
        while(tmp != null){
            System.out.println(tmp.value);
            tmp = tmp.next;//让tmp.next的地址回刷给tmp  即让tmp指向tmp的下一个结点
        }
    }

    //删除一个结点,返回值为true表示成功 反之失败
    public Boolean remove(Object val){
        if(head.value.equals(val)){//先判断头结点是否是要删除的结点
            head = head.next;//让头引用指向头结点的下一个
            size--;
            return true;
        }
        Node prev = head;
        while(prev.next != null){// 我们始终关注的都是prev.next
            if (prev.next.value.equals(val)){//对象的比较用equals
                //真的删除 的是prev.next
                if(prev.next == tail){//判断要删除的结点是否是尾结点
                    tail = prev;//让尾引用指向prev
                }
                 prev.next= prev.next.next;//让下一个结点的地址赋值给上一个结点的指针域
                size--;
                return true;
            }
            prev = prev.next;//将prev的next中的地址刷给prev 让prev指向这个地址
        }
        return false;
    }

    //记录几点个数
    public int size(){
//        链表的遍历是最慢的,尽量避免链表的遍历
//        int size = 0;
//        Node tmp = head;//设置一个引用
//        while (tmp != null){
//            size++;
//            tmp = tmp.next;
//        }
        return size;
    }

    //遍历查看以node为头结点的链表
    private void view(Node node){
        if(node == null){
            return;
        }
        System.out.println(node.value);
        view(node.next);
    }
    //递归的方式  递归简单 但是比遍历要慢
    public void travel2(){
        view(head);
    }
}

public class LinkTest {
    public static void main(String[] args) {
        Link link = new Link();
        link.add("99");
        link.add("9rt");
        link.add("9dg9");
        link.add("99gfh");
        link.add("9hg9");
        link.add("9h9");


        link.travel2();
        System.out.println(link.size);
        System.out.println(link.remove("9h9"));
        System.out.println("-------------------------");
        link.travel2();
        System.out.println(link.size);
        System.out.println("--------------------------");
        link.add("888");
        link.travel2();
        System.out.println(link.size);
    }



}

2、二叉树

左小右大的树

插入

插入过程分析

 插入代码实现

private TreeNode root;
//插入方法  递归
private void insert(TreeNode target,TreeNode newNode){//target 是指要要插入的那个结点的父节点
    if(newNode.value < target.value){//让目标结点和newNode结点作比较,如果小于 往左走
        if(target.left == null){//最好的情况,目标结点的右边是空
            target.left = newNode;//直接插入即可
        }else {
            insert(target.left,newNode);
        }

    }else{//往右走
        if(target.right == null){//最好的情况 ,目标结点的左边为空
            target.right = newNode;//直接插入
        }else {
            insert(target.right,newNode);
        }

    }
}
//添加方法
public void add(int val){
    TreeNode newNode = new TreeNode();
    newNode.value = val;
    if(root == null){//如果树为空,直接让这个结点做根节点
        root =newNode;
    }else{//树不空
        insert(root,newNode);
    }
}

遍历

用递归,二叉搜索树

内存分析

 遍历代码实现

private void view(TreeNode target){
    if(target == null){
        return;
    }
    view(target.left);//左
    System.out.println(target.value);//中
    view(target.right);//右
}
//遍历
public void travel(){
    view(root);
}

应用场景:检索

二分查找,每查找一次丢掉一半可能性

//二叉树搜索树 用于检索数据 搜索效率极高
public boolean check(TreeNode target,int val){
    if (target == null){
        return false;
    }
    if (val == target.value){
        return true;
    }
    if (val < target.value){
        return check(target.left,val);
    }else {
        return  check(target.right,val);
    }
}

public boolean contains(int val){
    return check(root,val);
}

3、抽象类

具体子类继承抽象父类时,必须实现(implement)全部抽象方法

实现(imlement) :由抽象变为具体

判别实现和不实现最简单的方式

看有没有方法体,有方法体 {} 就是实现

多态的思维方式:简化子类,抹杀子类的个性

抽象类可以使用final关键字声明吗?

不能,因为其需要子类继承

模板方法设计模式

让抽象类作为多个子类的通用模板

模板方法: 确定的,final修饰的

抽象方法:不确定的,暴露出去,强制子类必须实现


4、接口

具体类、抽象类、接口的区别?

具体类 : 某种事物抽象定义

抽象类 : 某类不同种事物抽象定义

接口(interface) : 不同类不同种事物共同的行为抽象定义.

接口的特点是什么呢?

1)使用关键字interface来修饰

2)接口中的方法全部都是公共抽象方法全局常量,不需要加public abstract,默认就有

全局常量:public static final int a = 10;

3)不可以有构造器、语句块

4)接口采用多继承机制

//接口
public interface Usb {
    void connect();
    void work();
    void disconnect();
}

为什么要有接口?

有时必须从几个类中派生出一个子类,继承他们所有的属性和方法,但是java中不支持多重继承。有了接口,就可以达到多重继承的效果。、

Java通过接口可以达到多重继承,为什么呢?

因为接口的纯粹性:全是抽象方法

类和接口的关系?

1)一个类可以实现多个接口

2)类不能继承接口, 接口也不能继承类, 接口不能实现类

只有一种情况 : 类(子类)实现接口(父类),并实现接口中所有抽象方法

3)具体类适用于接口的多态

4)接口也可以继承其他接口

理解

接口关注方法,突破了类型限制 ,类关注特征行为,有类型限制

接口不是类,类也不是接口,接口和类平级

注意点

1)实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则任为抽象类

package com.atguigu.javase.interfaceTest;

public class Udisk implements Usb{//Udisk类实现Usb接口
    @Override
    public void connect() {//重写方法里边要有具体实现内容
        System.out.println("我可以连接");
    }

    @Override
    public void work() {
        System.out.println("我可以工作");
    }

    @Override
    public void disconnect() {
        System.out.println("我可以断开连接");
    }
}

2)抽象类实现接口时,可以忽略接口中的抽象方法

3)子类必须先继承后实现,继承只能有一个,实现可以有多个

        (extends 后面的是亲爹,implements 后面的是干爹)

          接口用于定类的扩张功能,父类表明它是什么

打印接口引用指向的对象时,在接口中也没有写toString 方法,编译器为什么不报错呢?

System.out.println(f); //f是上图中的接口引用

因为编译器将其看作是对象了,由于接口永远不能本态 ,所以接口类型引用指向的一定是对象实体,就还是Object的子类。但这个引用只能用是父类Object中的方法。

小结:

接口用于表达某种能力,用于表示规范或标准,所以它里面都是公共方法。

接口可以引用子类对象,可多态,可虚方法调用

接口不能实例化new 接

排错练习


5、代理模式

面向接口编程

只把子类对象当成接口类型来看,对它的个性全部视而不见

低姿态:对对象要求低,只要完成目标怎么都行

什么是代理模式?

把代理对象当成被代理对象使用

(律师、租房)

使用场景

场景1:所用者无法直接创建被代理对象

(租房例子,降低要求)

场景2:对于被代理类有升级的要求,但是不可以修改被代理类

面向切面编程

Spring框架的内核思想就是面向切面,而切面就是通过代理对象对被代理对象方法的增强实现的,被代理对象是可以变化的,但无论怎么变,且切面、整个模式都是不变的,就好像是一个插件。

在代理对象、被代理对象、接口中哪个重要?

接口最重要!就像粘合剂。耦合度底

示例代码

package com.atguigu.javase.HomeWork;

/**
 * 代理模式:把代理对象当作代理对象来使用
 * 场景1:使用者无法直接创建代理对象
 * 场景2:对于代理类有升级需求,但是不可以修改被代理类,增加一个代理对象来间接升级
 */
interface ColMoney{
    void cm();
}

interface  HouseRent {
    void rent();
}

class FangDong1 implements  HouseRent{

    @Override
    public void rent() {
        System.out.println("我有房子要出租,毛坯房");
    }
}

class FangDong implements  HouseRent{

    @Override
    public void rent() {
        System.out.println("房子出租,拎包入住");
        
    }
}

class LianJia implements HouseRent,ColMoney{
    HouseRent fd = new FangDong();
    @Override
    public void rent() {
        System.out.println("请交中介费10000");
        fd.rent();//被代理对象如果变化,我也变,这里是切面,会濉新河代理对象的变化而变化
    }

    @Override
    public void cm() {
        System.out.println("交房租30000");
    }
}


public class ProxyTest {
    public static void main(String[] args) {
        HouseRent hr = new LianJia();
        hr.rent();
        ColMoney hr1 = new LianJia();
        hr1.cm();
    }
}


总结

今天学习的内容:

1)链表改进;

2)二叉树;

3)抽象类补充;

4)接口;

5)代理模式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值