精华内容
下载资源
问答
  • Java中,实现抽象的机制分两种,一抽象类,二接口。抽象类abstract class,接口Interface。今天学习一下Java中的抽象类和接口。目录什么是抽象什么是抽象类什么是接口区别代码例子结语什么是抽象从具体...

    36dd5c33f63387215b65570836c23445.png

    前言

    对于面向对象编程来说,抽象是它的特征之一。

    在Java中,实现抽象的机制分两种,一为抽象类,二为接口
    抽象类为abstract class,接口为Interface

    今天来学习一下Java中的抽象类和接口。

    目录

    1. 什么是抽象
    2. 什么是抽象类
    3. 什么是接口
    4. 区别
    5. 代码例子
    6. 结语

    什么是抽象

    从具体事物抽出,概括它们共同的方面,本质属性与关系等,称为抽象。看不见,摸不着的东西叫做抽象,抽象是人们对世界万物的感觉,用特定的图像表达出来,要理解抽象的东西,就必需从内心感受它们。

    举例,红色这个词语本身,在没有建立存在色彩这种概念以前,就是一个很抽象的东西。抽象说直白点就是把现实生活中复杂的问题抽象成一个简单的问题。举个例子,如人这个现实中的东西,抽象下就是person这个对象,对象有名字即类名,接着有属性即年龄,性别,身高等。对于对象中的方法即是人要吃饭,睡觉等。

    什么是抽象类

    抽象类的定义和使用格式

    abstract class 类名{
           类体
    }
    class 类名 extends 抽象类 {
              //实现全部抽象方法
    }

    抽象类是类对象的抽象集合,抽象类使用abstract关键字修饰,abstract不能与final并列修饰同一个类,在抽象类中具体抽象的是属性和行为,在抽象类中,含有抽象方法的类为抽象类,一定是抽象类,但抽象类中不一定含有抽象方法。

    抽象类不能被实例化,如果要实例化,需通过子类继承抽象类,然后子类必须重写父类的所有抽象方法。抽象方法在抽象类,仅此声明,方法的具体实现在子类中进行。抽象类只有被继承才有用武之地。

    //抽象类
    public abstract class Person {
     public abstract void run();
    }
    
    //子类继承抽象类
    public class Studentone extends Person{
     @Override
     public void run(){
      System.out.println("人会跑");
     }
    }
    
    //测试
    public class Test {
     public static void main(String[] args){
      Person person = new Student();
      person.run();
     }
    }
    
    //结果
    人会跑

    什么是接口

    接口的定义和使用格式

    interface 接口名 {
              [public] [static] [final] 变量;
              [public] [abstract] 方法;
    }
    class 类名 implement 接口列表 {
              //实现所有接口中声明的方法
    }

    接口是类行为方法的抽象集合,类行为方法,是对方法进行抽象,接口不是一个类,对于抽象类,它是对对象的抽象集合,对象即包含对属性和方法,接口是比抽象类更加抽象的一种。

    Java中的接口是为了降低功能模块间的耦合,子接口中所有方法都是抽象的,都是公有的。接口中的属性是全局常理即为public static final的,方法即public static的。关键字implements实现接口,Java中不能继承多个抽象类,但能实现多个接口,记住接口只能继承接口。

    interface Demo{
     void sleep();
     void play();
    }
    
    class Test implements Demo{
     void sleep();
     void play();
    }
    类必须实现接口中所有的方法,但抽象类可以不用实现接口中的所有方法。

    区别

    抽象类和接口语法层次

    public abstract class Demo {
        abstract void method1();
        void method2(){
        }
    }
    interface Demo {
        void method1();
        void method2();
    }

    可以看出接口是抽象类的特殊化。

    抽象类和接口设计层次

    1. 抽象类是对类抽象,而接口是对行为的抽象
    2. 抽象类为继承关系,接口为实现类与接口之间的契约关系
    3. 抽象类为单继承,接口为多“继承”。子类只能继承一个抽象类,但可实现多个接口。

    代码

    //抽象类
    abstract class Person {
     abstract void run();
     abstract void jump();
    }
    
    //接口
    interface School{
     void go();
    }
    
    //实现
    class Student extends Person implements School{
     void run();
     void jump();
     void go();
    }
    抽象类中成员变量默认为friendly,函数修饰符publicprotecteddefault
    接口属性为静态,public static final,成员函数为public

    结语

    • 详解Java抽象类和接口的区别

    小礼物走一走 or 点赞

    展开全文
  • 作者:学无止境来源:www.cnblogs.com/duanxz/p/3511695.html实现Serializable接口的目的是类可持久化,比如在网络传输或本地存储,系统的分布和异构部署提供先决条件。若没有序列化,现在我们所熟悉的远程调用...
    作者:学无止境来源:www.cnblogs.com/duanxz/p/3511695.html

    实现Serializable接口的目的是为类可持久化,比如在网络传输或本地存储,为系统的分布和异构部署提供先决条件。

    若没有序列化,现在我们所熟悉的远程调用,对象数据库都不可能存在,serialVersionUID适用于java序列化机制。

    简单来说,JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。

    在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。

    具体序列化的过程是这样的:序列化操作时会把系统当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会自动检测文件中的serialVersionUID,判断它是否与当前类中的serialVersionUID一致。

    如果一致说明序列化文件的版本与当前类的版本是一样的,可以反序列化成功,否则就失败;

    serialVersionUID有两种显示的生成方式:


    一是默认的1L,比如:private static final long serialVersionUID = 1L;        

    二是根据包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等诸多因子计算得出的,极度复杂生成的一个64位的哈希字段。基本上计算出来的这个值是唯一的。

    比如:private static final long  serialVersionUID = xxxxL;


    注意:显示声明serialVersionUID可以避免对象不一致,

    当一个类实现类Serializable接口,如果没有显示定义serialVersionUIDEclipse会自动给出相应的提醒;面对这种情况,我们只需要在Eclipse中点击类的warning图标,Eclipse就会自动给出两种生成方式。

    如果不想定义,在Eclipse的设置中也可以把它关掉的,设置如下:


    Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==> Potential programming problems

    将Serializable class without serialVersionUID的warning改成ignore即可。

    如果Class文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID也不会变化的。

    下面用代码说明一下serialVersionUID在应用中常见的几种情况。


    (1)序列化实体类

    package com.example.demo.entity.serializable;
     
    import java.io.Serializable;
     
    public class Persion implements Serializable {
     
        private static final long serialVersionUID = 4359709211352400087L;
        public Long id;
        public String name;
        public final String userName;
     
        public Persion(Long id, String name){
            this.id = id;
            this.name = name;
            userName = "dddbbb";
        }
     
        public String toString() {
            return id.toString() + "--" + name.toString();
        }
    }

    (2)序列化功能:

    package com.example.demo.entity.serializable;
     
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
     
    public class SerialTest {
     
        public static void main(String[] args) {
            Persion p = new Persion(1L"陈俊生");
            System.out.println("person Seria:" + p);
            try {
                FileOutputStream fos = new FileOutputStream("Persion.txt");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                oos.writeObject(p);
                oos.flush();
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    (3)反序列化功能

    package com.example.demo.entity.serializable;
     
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.ObjectInputStream;
     
    public class DeserialTest {
     
        public static void main(String[] args) {
            Persion p;
            try {
                FileInputStream fis = new FileInputStream("Persion.txt");
                ObjectInputStream ois = new ObjectInputStream(fis);
                p = (Persion) ois.readObject();
                ois.close();
                System.out.println(p.toString());
                System.out.println(p.userName);
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    情况一:Persion类序列化之后,从A端传到B端,然后在B端进行反序列化,在序列化Persion和反序列化Persion的时候A和B端都需要一个相同的类。如果两处的serialVersionUID不一致,会产生什么样的效果呢。


    【答案】可以利用上面的代码做个试验来验证:

    先执行测试类SerialTest,生成序列化文件,代表A端序列化后的文件,然后修改serialVersion值,再执行测试类DeserialTest,代表B端使用不同serialVersion的类去反序列化,结果报错:

    java.io.InvalidClassException: com.example.demo.entity.serializable.Persion; local class incompatiblestream classdesc serialVersionUID = 4359709211352400087, local class serialVersionUID = 4359709211352400082
      at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
      at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
      at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
      at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
      at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
      at com.example.demo.entity.serializable.DeserialTest.main(DeserialTest.java:22)

    情况二:假设两处serialVersionUID一致,如果A端增加一个字段,然后序列化,而B端不变,然后反序列化,会是什么情况呢?

    package com.example.demo.entity.serializable;
     
    import java.io.Serializable;
     
    public class Persion implements Serializable {
     
        private static final long serialVersionUID = 4359709211352400082L;
        public Long id;
        public String name;
        public int age;
     
        public Persion(Long id, String name, int age){
            this.id = id;
            this.name = name;
            this.age = age;
        }
     
        public String toString() {
            return id.toString() + "--" + name.toString() + "age:" + age;
        }
    }
    package com.example.demo.entity.serializable;
     
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
     
    public class SerialTest {
     
        public static void main(String[] args) {
            Persion p = new Persion(1L"陈俊生"100);
            System.out.println("person Seria:" + p);
            try {
                FileOutputStream fos = new FileOutputStream("Persion.txt");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                oos.writeObject(p);
                oos.flush();
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    Person DeserialPerson:1--陈俊生

    【答案】新增 public int age; 执行SerialTest,生成序列化文件,代表A端。删除 public int age,反序列化,代表B端,最后的结果为:执行序列化,反序列化正常,但是A端增加的字段丢失(被B端忽略)。
    情况三:假设两处serialVersionUID一致,如果B端减少一个字段,A端不变,会是什么情况呢?

    package com.example.demo.entity.serializable;
     
    import java.io.Serializable;
     
    public class Persion implements Serializable {
     
        private static final long serialVersionUID = 4359709211352400082L;
        public Long id;
        public String name;
    // public int age;
     
        public Persion(Long id, String name){
            this.id = id;
            this.name = name;
    // this.age = age;
        }
     
        public String toString() {
            return "Persion:"+id.toString() + "name:" + name.toString() ;
        }
    }

    Person DeserialPerson: 1234,age:0

    【答案】序列化,反序列化正常,B端字段少于A端,A端多的字段值丢失(被B端忽略)。

    情况四:假设两处serialVersionUID一致,如果B端增加一个字段,A端不变,会是什么情况呢?


    验证过程如下:


    先执行SerialTest,然后在实体类Person增加一个字段age,如下所示,再执行测试类DeserialTest.

    package com.sf.code.serial;
     
    import java.io.Serializable;
     
    public class Person implements Serializable {
        private static final long serialVersionUID = 1234567890L;
        public int id;
        public String name;
        public int age;
     
        public Person(int id, String name) {
            this.id = id;
            this.name = name;
        }
        
        /*public Person(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }*/

     
        public String toString() {
            return "Person: " + id
                    + ",name:" + name
                    + ",age:" + age;
        }
    }

    结果:Person DeserialPerson: 1234,name:wang,age:0


    说明序列化,反序列化正常,B端新增加的int字段被赋予了默认值0。


    最后通过下面的图片,总结一下上面的几种情况。

    052ae44c1045e6630ac3bb68a14074da.png

    package com.example.demo.entity.serializable;
     
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
     
    import org.aspectj.weaver.ast.Test;
     
    public class TestStatic implements Serializable {
     
        private static final long serialVersionUID = 1L;
        public static int staticVar = 5;
     
        public static void main(String[] args) {
            try {
                // 初始时staticVar为5
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
                out.writeObject(new TestStatic());
                out.close();
                // 序列化后修改为10
                TestStatic.staticVar = 10;
                ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
                TestStatic t = (TestStatic) oin.readObject();
                oin.close();
                // 再读取,通过t.staticVar打印新的值
                System.out.println(t.staticVar);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    另外送下福利,大家可以关注Java核心技术公众号在后台回复福利可以获取一份最新Java面试题资料。

    清单 2 中的 main 方法,将对象序列化后,修改静态变量的数值,再将序列化对象读取出来,然后通过读取出来的对象获得静态变量的数值并打印出来。依照清单 2,这个 System.out.println(t.staticVar) 语句输出的是 10 还是 5 呢?

    最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

    父类的序列化与 Transient 关键字

    情境:一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象,然后反序列化后输出父类定义的某变量的数值,该变量数值与序列化时的数值不同。

    解决:要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就 需要有默认的无参的构造函数。

    在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。

    所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。

    如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

    Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

    特性使用案例

    我们熟悉使用 Transient 关键字可以使得字段不被序列化,那么还有别的方法吗?根据父类对象序列化的规则,我们可以将不需要被序列化的字段抽取出来放到父类中,子类实现 Serializable 接口,父类不实现,根据父类序列化规则,父类的字段数据将不被序列化,形成类图如图 2 所示。

    d98b2ba5b35a88ce0ed33f726f76d65f.png

    上图中可以看出,attr1、attr2、attr3、attr5 都不会被序列化,放在父类中的好处在于当有另外一个 Child 类时,attr1、attr2、attr3 依然不会被序列化,不用重复抒写 transient,代码简洁。

    static final 修饰的serialVersionUID如何被写入到序列化文件中的,

    看下面的源码:

    序列化写入时的ObjectStreamClass.java中,

    void writeNonProxy(ObjectOutputStream out) throws IOException {
            out.writeUTF(name);
            out.writeLong(getSerialVersionUID());

            byte flags = 0;
            ...
    public long getSerialVersionUID() {
            // REMIND: synchronize instead of relying on volatile?
            if (suid == null) {
                suid = AccessController.doPrivileged(
                    new PrivilegedAction() {public Long run() {return computeDefaultSUID(cl);
                        }
                    }
                );
            }return suid.longValue();
        }

    595978d5dc479e0e551df8d5e2ea61ee.png

    最后给大家送下福利,大家可以关注Java核心技术公众号,在后台回复 “福利”可以获取一份我整理的最新Java面试题资料。(END)

    最近好文分享

    Hyperf+RabbitMQ+WebSocket消息推送还在用Swagger(丝袜哥)生成接口文档?是真的猛!SQL 语法速成手册,要考!List 如何一边遍历,一边删除?碰到Maven依赖冲突怎么办?Spring 和 Spring Boot 最核心的 3 大区别6月全国程序员工资新统计,太扎心了!10 个雷人的注释,就怕你不敢用!

    更多请扫码关注 Java核心技术

    e09f91259688f95f4950c27e18f352ba.png

    一个分享Java核心技术干货的公众号欢迎大家在看、转发
    展开全文
  • Java中的实现底层二叉树创建和遍历,利用IDEA 看了形形色色的资料,要么都是C、C++的版本,对于初学java的人说,有点难度,因此我花了几天时间总结了这篇文章,希望对大家有点。 1.首先了解一些简单的二叉树知识...

    看了形形色色的资料,要么都是C、C++的版本,对于初学java的人来说,有点难度,因此我花了几天时间总结了这篇文章,希望对大家有点用。

    1.首先了解一些简单的二叉树知识:

    1)二叉树是一种数据结构;"数据结构+算法=程序"的概念必须深深的印在脑海里,这也是我们为什么学习数据结构与算法的根本原因;了解数据结构才能在写出更好的程序;
    这里放一张图来加深对二叉树的理解:

    这幅图是随便找的,但我觉得可以给大家很好的体验,因为第一张图1是根节点,是23的父节点,而23之间湖成为堂兄弟;45是2的孩子,45之间是兄弟关系,6是3的孩子,6和45互为堂兄弟关系,这个跟我们日常生活相差无几,理解即可。
    第二幅图:除父节点之外都是单节点,也就是说父节点1有两个子节点2 3,而2只有一个右节点(或者成为右孩子rightchild),4只有一个左节点(也就是左孩子leftchild),35关系同上。
    两幅图中,456 65分别是终结点,也就是树的叶子,所以又叫叶节点

    2)二叉树的基本单元是结点(包括存储的数据和左右子树的存储地址)
    3)二叉树的一些名称:自平衡二叉树\红黑树\完全二叉树\满二叉树…
    4)二叉树的遍历方法:(四种)
    前序遍历
    中序遍历
    后序遍历
    层序遍历
    5)由中序遍历和其他任意一种遍历可以唯一确定一颗二叉树(原因是在当只有一个子节点存在时,前后序只能确定父子关系,而不能确定左右根之间的关系,可以自己画图理解,这里不再赘述)
    6)代码实现:
    1.首先简单的说明一下,在IDEA工具箱中,先创建一个包package:com.huster.chapter5tree.TreeTry(这个可以大家自行搞定,如果想要直接复制我的代码,需要进行本步骤)
    2.本代码说明:分为两大类 a)创建二叉树的基本单元TreeNode并封装,提供插入数据和前中后序遍历的静态方法 b)测试类Test ,将{63 45 77 66 86 62 42 48}插入二叉树,并进行三种遍历方式
    3.直接上代码!
    先上一个TreeNode类

    package com.huster.chapter5Tree.TreeTry;
    
    public class TreeNode {
        //属性私有化 父节点\左节点\右节点\存储的数据
        private TreeNode leftchild;
        private TreeNode rightchild;
        private TreeNode rootnode;
        private int data;
    
    
        //构造器
        public TreeNode(TreeNode leftchild, TreeNode rightchild, int data) {
            this.leftchild = leftchild;
            this.rightchild = rightchild;
            this.data = data;
        }
    
        public TreeNode(int data) {
            this.data = data;
            this.leftchild = null;
            this.rightchild = null;
        }
    
        public TreeNode() {
        }
    
        //setter and getter
        public TreeNode getLeftchild() {
            return leftchild;
        }
    
        public TreeNode getRightchild() {
            return rightchild;
        }
    
        public TreeNode getPrerant() {
            return rootnode;
        }
    
        public int getData() {
            return data;
        }
    
        public void setLeftchild(TreeNode leftchild) {
            this.leftchild = leftchild;
        }
    
        public void setRightchild(TreeNode rightchild) {
            this.rightchild = rightchild;
        }
    
        public void setPrerant(TreeNode prerant) {
            this.rootnode = prerant;
        }
    
        public void setData(int data) {
            this.data = data;
        }
    
    
    
        //插入数据
        public static TreeNode insert(TreeNode rootnode, int value) {
            if (value<rootnode.getData()){
                if (null==rootnode.getLeftchild()){
                    TreeNode temp=new TreeNode(value);
                    temp.setPrerant(rootnode);
                    rootnode.setLeftchild(temp);
                    return temp;
                }else return insert(rootnode.getLeftchild(),value);
            }else{
                if (null==rootnode.getRightchild()){
                    TreeNode temp=new TreeNode(value);
                    temp.setPrerant(rootnode);
                    rootnode.setRightchild(temp);
                    return temp;
                }else return insert(rootnode.getRightchild(),value);
            }
        }
    
    
    
        //前序遍历
        public static void preOrd(TreeNode treeNode){
    
                if(null==treeNode){
                    return;
                }
                System.out.print(treeNode.data+" ");
                preOrd(treeNode.leftchild);
                preOrd(treeNode.rightchild);
            }
        //中序遍历
        public static void inOrd(TreeNode treeNode){
                if (treeNode==null){
                    return;
                }
                inOrd(treeNode.leftchild);
                System.out.print(treeNode.data+" ");
                inOrd(treeNode.rightchild);
    
        }
        //后序遍历
        public  static void postOrd(TreeNode treeNode){
            if (null==treeNode){
                return;
            }
            postOrd(treeNode.leftchild);
            postOrd(treeNode.rightchild);
            System.out.print(treeNode.data+" ");
        }
        //层序遍历
        public static void levelOrd(TreeNode rootnode) {
            if (rootnode == null)
                return;
            LinkedList<TreeNode> lists= new LinkedList<>();//Queen<TreeNode> queen=new LinkedBlockingQueen<>();
            TreeNode curr;
            lists.add(rootnode);
            while (!lists.isEmpty()) {
                curr = lists.pop();//查API文档,此处也可以用LinkedBlockingQueen的poll()方法或者直接用LinkedList的pop()方法
                System.out.print(curr.getData()+ " ");
                if (curr.leftchild != null)
                    lists.add(curr.leftchild);
                if (curr.rightchild != null)
                    lists.add(curr.rightchild);
            }
        }
    
        @Override
        public String toString() {
            return "TreeNode{" +
                    "data=" + data +
                    '}';
        }
    }
    
    

    然后是测试类Test`

    package com.huster.chapter5Tree.TreeTry;
    
    public class Test {
        public static void main(String[] args) {
            TreeNode rootnode=new TreeNode(63);
            TreeNode.insert(rootnode,45);
            TreeNode.insert(rootnode,77);
            TreeNode.insert(rootnode,66);
            TreeNode.insert(rootnode,86);
            TreeNode.insert(rootnode,62);
            TreeNode.insert(rootnode,42);
            TreeNode.insert(rootnode,48);
            System.out.print("前序遍历的结果:");
            TreeNode.preOrd(rootnode);
            System.out.println();
            System.out.print("中序遍历的结果:");
            TreeNode.inOrd(rootnode);
            System.out.println();
            System.out.print("后序遍历的结果:");
            TreeNode.postOrd(rootnode);
        }
    }
    

    以上就是全部过程,基本知识点和代码结合理解。如果大家有任何问题,可以在评论区留言。

    展开全文
  • 1,为什么spring,Spring主要使用了什么模式?spring能够很好的和各大框架整合,它通过IOC容器管理了对象的创建和销毁 工厂模式。在使用hiberna,mybatis的时候,不用每次都编写提交的事务的代码,可以使用spring...

    今天继续为大家整理Java面试题,并涉及数据库和网络等相关知识,希望能帮助到各位开发者。

    084d4b01a85976ebdf4c74d0a04baba7.png

    1,为什么要用spring,Spring主要使用了什么模式?

    spring能够很好的和各大框架整合,它通过IOC容器管理了对象的创建和销毁  工厂模式。在使用hiberna,mybatis的时候,不用每次都编写提交的事务的代码,可以使用spring的AOP来管理事务。AOP其实就是一个动态代理的实现(声明式事务和编程式事务)。

    主要使用了模式:

    工厂模式:每个Bean的创建通过方法;

    单例模式:默认的每个Bean的作用域都是单例;

    代理模式:关于Aop的实现通过代理模式;

    2、Mybatis工作原理?

    07d5f6c8fbc1fcc67d32650e0418f111.png

    原理:

    • 通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory。

    • SqlSessionFactory开启一个SqlSession,通过SqlSession实例获得Mapper对象并且运行Mapper映射的Sql语句。

    • 完成数据库的CRUD操作和事务提交,关闭SqlSession。

    3,mybatis的优缺点?

    优点:SQL写在XML中,便于统一管理和优化

               提供映射标签,支持对象和数据库的orm字段关系映射

               可以对SQL进行优化

    缺点: SQL工作量大

                mybagtis移植姓不好

                不支持级联 

    4,maven是什么?有什么作用?

    是一个项目管理、构建工具

    作用:帮助下载jar   寻找依赖,帮助下载依赖   热部署、热编译。

    5,什么RESTful架构?

    1)每一个URI代表一种资源;

    2)客户端和服务器之间,传递这种资源的某种表现层;

    3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化",

    6,说说tcp/ip协议族

    TCP/IP协议族是一个四层协议系统,自底而上分别是数据链路层、网络层、传输层和应用层。每一层完成不同的功能,且通过若干协议来实现,上层协议使用下层协议提供的服务。

    1)数据链路层负责帧数据的传递。

    2)网络层责数据怎样传递过去。

    3)传输层负责传输数据的控制(准确性、安全性)

    4)应用层负责数据的展示和获取

    7,说说tcp三次握手,四次挥手。

    0940283d68b3ebecf112bef37c928306.png

    TCP的连接建立是一个三次握手过程,目的是为了通信双方确认开始序号,以便后续通信的有序进行。主要步骤如下:

    1)连接开始时,连接建立方发送SYN包,并包含了自己的初始序号a;

    2)连接接受方收到SYN包后会回复一个SYN包,其中包含对上一个a包的回应信息ACK,回应的序号为下一个希望收到包的序号,即a+1,然后还包了自己的初始序号b;

    3. 连接建立方(Client)收到回应的SYN包以后,回复一个ACK包做响应,其中包含了下一个希望收到包的序号即b+1。

    8459978c76028627945be4cbc1658663.png

    TCP终止连接的四次握手过程如下:

    1. 首先进行关闭的一方(即发送第一个FIN)将执行主动关闭,而另一方(收到这个FIN)执行被动关闭。

    2. 当服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。

    3. 同时TCP服务器还向应用程序(即丢弃服务器)传送一个文件结束符。接着这个服务器程序就关闭它的连接,导致它的TCP端发送一个FIN。

    4. 客户必须发回一个确认,并将确认序号设置为收到序号加1。

    8,GIT和SVN的区别

    主要区别如下:

    1)GIT是分布式的,SVN不是。

    2)GIT把内容按元数据方式存储,而SVN是按文件。

    3)GIT分支和SVN的分支不同。

    4)GIT没有一个全局的版本号,而SVN有。

    5)GIT的内容完整性要优于SVN。

    9,BIO、NIO和AIO的区别

    Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

    Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

    Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

    NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间)。

    AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。

    10,为什么要用线程池?

    线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。

    使用线程池的好处:

    1、线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。

    2、线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。

    3、线程池根据当前在系统中运行的进程来优化线程时间片。

    4、线程池允许我们开启多个任务而不用为每个线程设置属性。

    5、线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。

    6、线程池可以用来解决处理一个特定请求最大线程数量限制问题。

    11,悲观锁和乐观锁的区别,怎么实现?

    悲观锁:一段执行逻辑加上悲观锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。

    乐观锁:一段执行逻辑加上乐观锁,不同线程同时执行时,可以同时进入执行,在最后更新数据的时候要检查这些数据是否被其他线程修改了(版本和执行初是否相同),没有修改则进行更新,否则放弃本次操作。

    悲观锁的实现:

    41ed623b05dce0e916cb1c5a6c67b350.png

    乐观锁的实现:

    995fe82c8b0339ae8b88ba4d7a0a3fc0.png

    12,java中的堆和栈分别是什么数据结构,为什么要分为堆和栈来存储数据?

    栈是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。堆是一种经过排序的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常用来实现优先队列,堆的存取是随意的。

    为什么要划分堆和栈:

    1)从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。

    2)堆与栈的分离,使得堆中的内容可以被多个栈共享。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。

    3)栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。

    4)体现了Java面向对象这一核心特点(也可以继续说一些自己的理解)。

    fa940118dd12152b73e881a4ec4960db.png

    展开全文
  • 面试官:为什么要重写hashcode和equals方法? 这才是学编程要懂的基础! 真有小伙伴不知道浮点数如何转二进制吗? List使用踩坑记录 Map使用踩坑记录 多线程/并发 ThreadLocal源码剖析 线程池详解 各种“锁”大...
  • 我调研了 100 Java 开源博客系统,发现这 5 个最好! 权限管理系统 Guide 哥注:权限管理系统在企业级的项目中一般都是非常重要的,如果你需求去实际了解一个不错的权限系统是如何设计的话,推荐你可以参考...
  • 但很多人选择Python写爬虫,为什么呢?因为Python确实很适合做爬虫,丰富的第三方库十分强大,简单几行代码便可实现你想要的功能。更重要的,Python也是数据挖掘和分析的好能手。那么,Python爬虫一般什么框架...
  • 序列化对象Studentmain实现截图序列化集合反序列化反序列化对象反序列化集合判断反序列化是否是集合反序列化集合代码注意transientStudent类注意反序列化代码序列化代码执行结果nameNULL解释:序列化版本号有什么...
  • 并且IDEA在界面组件的拖拽功能上好像不如NetBeans强大(也可能是我学艺不精呀),反正感觉IDEA的组件着挺难受的,正好自己看了一个星期的视频,还没有什么可以实践的地方,刚好就可以这个作业实践一下。...
  • 虽然直到目前 B3log 系产品用户不多,但我们已经初步证明了:Java 用来实现博客、论坛没有什么不好的 使用开源软件,了解开源思想,融入开源生态 如果你想做个程序员相关的论坛,请三思 你怎么看待社群、社区这...
  • 序列化是一种对象持久化的手段。普遍应用在网络传输、RMI等场景中。...为什么《阿里巴巴Java开发手册》中有以下规定:背景知识在展开本文的介绍之前,先简单介绍一些和序列化有关的知识,Serializab...
  • 为什么PUSH推送经常出事故? 三歪了10分钟写完了一个需求 :book:Java容器 Java集合总结 【新手向】如何学习Java集合 Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是...
  • 4.1、为什么日志? 4.2、常见的日志框架 4.3、日志级别 4.4、日志文件的组成 4.5、日志的使用 五、OGNL 六、作用域和生命周期 MyBatis文档 一、 走进MyBatis 跳转到目录 1.1、什么是框架? 跳转到目录 ...
  • 1、为什么说Spring是一个一站式的轻量级开源框架呢? 2、Spring的优点 3、Spring的体系结构 三、IoC和DI思想 四、 Spring初体验 1、什么是BeanFactryo? 2、Spring IoC管理bean的原理 3、Spring的基本配置 4、...
  • 实战系列02:包学会,教你用Java函数式编程重构烂代码 实战系列03:请避开Stream流式编程常见的坑 实战系列04:使用Java8 Optional类优雅解决空指针问题 数据结构和算法 学什么? 有些同学可能要问了:我学 Java ...
  • 为什么呢?我的理解Hibernate是JPA的一种实现,更加的标准。 3.5 微服务 这年头如果你不提微服务好像就低人一等。不用怕,Spring特意为我们准备了微服务全家桶Spring Cloud。 并不是所有的服务都需要微服务,微服务...
  • JEECG业务流程: 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计(松耦合)、并支持任务节点灵活配置,既...
  • 不会用Java Future,我怀疑你泡茶没我快 有比 ReadWriteLock更快的锁?全面解析StampedLock, 了解后瞬间升级 搞定 CompletableFuture,并发异步编程和编写串行程序还有什么区别? ExecutorService VS ...
  • 来来来,我们聊一聊,为什么不建议使用递归操作? 调度服务 ScheduledExecutorService 经常卡顿问题的排查及解决方法 记一次 JVM CPU 使用率飙高问题的排查过程 如何画出一张合格的技术架构图? 详述 Elasticsearch ...
  • Android插件

    2018-08-27 10:41:24
    所以,这种有规律又重复简单的工作为什么不能一个插件来实现呢?于是RemoveButterKnife的想法就出现了。 具体介绍 23.AndroidProguardPlugin 一键生成项目混淆代码插件,值得你安装~(不过目前可能有些第三方...
  • 13.为什么问题诊断和排查这么困难【已完成】 14.JVM性能指标监控工具 -- Micrometer【已完成】 15.DataDog集成MySQL的配置【已完成】 16.迁移Ubuntu下MySQL的data目录【已完成】 17.Java坑人面试题系列: 变量...
  • 为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较? fastjson到底做错了什么?为什么会被频繁爆出漏洞? 大厂是如何设计接口的? 不要再在对外接口中使用枚举类型了! 科普 你知道程序是怎么处理时区...
  • 为什么有时候会发现消息发送到了队列中,但是不被消费? A: 一种可能的原因是。 你的电脑上在Debug一个程序jseckill-backend, 另外在你自己的服务器上也运行了同样的程序。 两个程序如果连接的是同一个RabbitMQ,就...
  • 互联网架构为什么要做服务化 微服务架构技术栈 分布式理论:CAP 是三选二吗? 使用LCN框架解决分布式事物 百度开源的分布式 id 生成器 大型分布式电商系统的订单生成策略 分布式事务综述 分布式事务...
  • 更多内容可以查看我的这篇原创文章:我为什么推荐你写博客? 多用 Github 没事多去 Github 转转,如果有能力可以参与到一些开源项目中。多看看别人开源的优秀项目,看看别人的代码和设计思路,看的多了,你的编程...
  • 《SpringBoot-2.3镜像方案为什么要做多个layer》 《体验SpringBoot(2.3)应用制作Docker镜像(官方方案)》 《详解SpringBoot(2.3)应用制作Docker镜像(官方方案)》 《掌握SpringBoot-2.3的容器探针:基础篇》 《掌握...
  • 为了循序渐进,最初的是时候,我是基于传统的 BIO 的方式 Socket 进行网络传输,然后利用 JDK 自带的序列化机制 来实现这个 RPC 框架的。后面,我对原始版本进行了优化,已完成的优化点和可以完成的优化点我都列在了...
  • Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现。 Spring boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud...

空空如也

空空如也

1 2
收藏数 36
精华内容 14
关键字:

为什么idea用java来实现

java 订阅