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)代理模式