JAVA面试八股文合集

目录

1.Java基础

1.1 面向对象的特征有哪些?(面向对象)

 1.2ArrayList和LinkedList的区别是什么?(java集合)

1.3高并发中的集合有哪些问题?(多线程与数据结构)

1.4 什么是线程安全与不安全?(多线程)

1.5java虚拟机的内存结构(JVM)

1.6二分查找?(数据结构)

1.7单例模式方法一--饿汉式?(设计模式)

1.8单例模式方法二--懒汉式?(设计模式)

1.9并行与并发的区别?(操作系统、多线程)

1.10 程序计数器是什么?(JVM)

1.11 详细介绍java堆?(JVM)

1.12排序算法(数据结构)

1.12.1冒泡排序

1.13如何防止死锁?(多线程)

1.14什么是守护线程?(多线程)

1.15 使用多线程,模拟实现红灯,绿灯,和黄灯的功能红灯--打出5个数字,接着黄灯亮--打印3个数字,接着绿灯亮--打印5个数字(多线程)

1.16java中的wait()和sleep()方法有什么不同?

1.17Java中 Integer和int的区别?

1.18值传递与引用传递问题

1.19java中线程的状态有哪些?

1.20二叉树展开为链表(算法)

1.21 jsp八大隐含对象

1.22 编写程序实现1到100万中间素数个数的计算

1.23多线程中start()和run()方法的区别是什么?

1.24创建线程有哪几种方式?

1.25H2O的生成(线程的同步)

2.计算机网络

2.1 OSI七层模型

2.2.什么是网络四元组?

3.操作系统与linux

3.1线程与进程的区别?

4.数据库相关

4.1数据库事务的隔离级别?

4.2数据库的三大范式

4.3.数据库事务的特性?

5.框架相关

5.1Spring中常见的注解有哪些?

5.2Spring MVC中常用注解有哪些?

5.3Spring  Boot中常用注解有哪些?

5.4 Spring MVC的执行流程?

5.5Spring中BeanFactory和FactoryBean的区别?


1.Java基础

1.1 面向对象的特征有哪些?(面向对象)

封装(可复用性)

   属性封装:属性私有化;设计相关的公共getter和setter来达成对属性的间接访问。

   逻辑封装:隐藏具体实现,提供对外方法接口供用户调用。

继承(可复用性)

   从已有类中派生出新的类,新的类能够吸收已有的类的数据属性和行为,并能扩展新的能力。

多态(灵活性):两个类存在继承关系,存在方法的重写,在调用时,有父类引用指向子类对象
 

 1.2ArrayList和LinkedList的区别是什么?(java集合)

ArrayList和LinkedList两者都实现了List接口,但它们的底层不同。

(1) .ArrayList的底层是用数组实现的,而LinkedList的底层是链表。

(2).因为ArrayList的底层是数组,而数组是基于索引的数据结构。因此ArrayList查找数据是很快的,时间复杂度为O(1),但删除和插入的开销很大。

(3)因为LinkedList的底层是链表,所以,LinkedList在插入数据和删除数据时的开销优于ArrayList,但在查询搜索数据时的开销比ArrayList大。

(4)LinkedList需要更多的内存(链表的节点在内存中是不连续的)。

(5)读少,插入删除多推荐用LinkedList;读多,插入删除少,推荐用ArrayList。(不是绝对的)

1.3高并发中的集合有哪些问题?(多线程与数据结构)

(1)第一代线程安全集合类

(2)第二代线程非安全集合类

底层使用synchronized代码块锁。

(3)第三代线程安全集合类

java.util.concurrent.*

ConcurrentHashMap:

CopyOnWriteArrayList:

CopyOnWriteArraySet:

底层大都采用Lock锁,保证线程安全的同时,性能也很高。

1.4 什么是线程安全与不安全?(多线程)

当多线程时,才存在线程安全问题。在堆内存中的数据可以被任何线程访问到,在没有限制的情况下,存在被意外修改的风险,即堆内存的空间在没有保护机制的情况下,对于多线程来说,是不安全的。

解决方法: 1.私有化(放到栈内存中,例如局部变量,缺点:缩小了使用范围)---隔离

                   2.不共享(ThreadLocal类,每个线程各自拷贝一份)--隔离  

                   3.只能看不能改(final)--标记

                   4.先入为主(线程的同步【互斥锁(悲观锁)】线程很多时)--标记

                   5.失败重试(CAS乐观锁)-->线程较少时

1.5java虚拟机的内存结构(JVM)

如下图:蓝色的为共享,橙色的是私有

1.方法区:存类的信息

2.堆:存对象的信息

3.虚拟机会创建一个名字叫main的主线程(即程序中的main方法),主线程的内存由JVM Stacks分配(JVM Stacks也负责分配线程本身的内存)

4.自定义方法内的局部变量和方法参数等利用的是栈的内存(JVM Stacks)

5.本地方法(例如:HashCode()等)引用本地方法栈

6.程序计数器:通俗地来说,就是用来记录当前的线程执行到了第几行代码。

1.6二分查找?(数据结构)

前提:已有有序数组A

       1.定义左边界left,右边界right,确定搜索范围,循环执行二分查找(2,3)

       2.获取中间索引middle=Floor((left+right)/2).

       3.中间索引的值与待搜索的值T进行比较:

                  case 1: A[middle]==T,返回中间索引

                  case 2:A[middle]>T,说明待查找的值在middle左边,需要将右边界设置为(middle-1),重新查找。      

                  case 3:A[middle]<T,说明待查找的值在middle右边,需要将左边界设置为(middle+1),重新查找。      

        4.若L>R时,表示没有找到,应当结束循环。

代码实现:

public class Solution {
    public static void main(String[] args) {
        int[] array={1,5,8,11,19,22,31,35,40,45,48,49,50};
        int target=48;
        int indx=binarySearch(array,target);
        System.out.println(indx);
    }

    public static  int binarySearch(int[] a,int target){
      int left=0,right=a.length-1,middle;
      while (left<=right){
          middle=(left+right)/2;
          if(a[middle]==target){
              return middle;
          } else if (a[middle]>target) {
              right=middle-1;
          }else {
              left=middle+1;
          }
      }

      return -1;

    }
}

问题:以上,当left+right数值很大,溢出整数范围时应当如何解决?

   解决方法:将中间索引赋值改为:middle=left+(right-lft)/2或(right+left)>>>1

例一:对于有序列表1,5,8,11,19,22,31,35,40,45,48,49,50当二分查找48时,查找成功需要比较几次?

 思路:1.长度为奇数二分取中间的值;偶数二分取中间靠左的值。

例二:在拥有128个元素的数组中二分查找一个数,需要比较的次数最多不超过多少次?

思路一:128一直除2,直到1,中间除了几次,答案就是几;即2的几次方等于128,答案就是几

思路二:将问题转发为l2g128的对数运算。如果结果是整数,则为最终结果;如果结果为小数,则舍去小数部分,整数加一作为最终结果。

1.7单例模式方法一--饿汉式?(设计模式)

要点:构造私有;提供静态的成员单例类型属性(私有),并要求在声明时实例化;提供一个公共的静态方法getInstance()用于访问(获得)实例。代码如下:

public class SingletonOne {
    private static final SingletonOne INSTANCE=new SingletonOne();
    private SingletonOne(){
        
    }
    public static SingletonOne getInstance(){
        return INSTANCE;
    }
}

饿汉式单例的特点:单例类实例提前创建。

注意事项:单例模式的单例不是安全的。可以通过以下途径破坏

                  1.如果单例对象实现了Serializable接口,单例可能会被破坏;这种情况就是利用反序列化可能破坏单例。预防方法:重写public Object readResolve()方法,代码如下:

public class SingletonOne implements Serializable {
    private static final SingletonOne INSTANCE=new SingletonOne();
    private SingletonOne(){
        if(INSTANCE!=null){
            throw new RuntimeException("单例对象不能重复创建");
        }
        ......
    }
    public static SingletonOne getInstance(){
        return INSTANCE;
    }
    public Object readResolve(){
        return INSTANCE;
    }
}

                  2.可以通过反射直接调用私有的构造方法破坏单例。预防方法:在构造方法例加入判断来判断是否单例,代码如下:

public class SingletonOne {
    private static final SingletonOne INSTANCE=new SingletonOne();
    private SingletonOne(){
        if(INSTANCE!=null){
            throw new RuntimeException("单例对象不能重复创建");
        }
        //
        ......
    }
    public static SingletonOne getInstance(){
        return INSTANCE;
    }
}

                3.Unsafe对象破坏单例,无法预防。

1.8单例模式方法二--懒汉式?(设计模式)

要点:构造私有;提供静态的成员单例类型属性(私有);提供一个公共的静态方法getInstance()用于访问(获得)实例。代码如下:

懒汉式单例模式代码如下:

public class SingletonTwo implements Serializable {
    private static SingletonTwo INSTANCE=null;
    private SingletonTwo(){
        

    }
    public static SingletonTwo getInstance(){
        if(INSTANCE==null){
            INSTANCE=new SingletonTwo();
        }
        return INSTANCE;
    }
    public Object readResolve(){
        return INSTANCE;
    }
}

懒汉式单例需要考虑线程安全问题。需要在方法前面加synchronized关键字,如下:

public class SingletonTwo implements Serializable {
    private static SingletonTwo INSTANCE=null;
    private SingletonTwo(){
      

    }
    public static synchronized SingletonTwo getInstance(){
        if(INSTANCE==null){
            INSTANCE=new SingletonTwo();
        }
        return INSTANCE;
    }
    public Object readResolve(){
        return INSTANCE;
    }
}

但以上方法性能较差。实际是首次创建单例对象时需要提供线程安全得保护,再此调用时并不需要加synchronized关键字进行保护,解决方法是使用DCL(双端检索)代码如下:

public class SingletonTwo implements Serializable {
    private static volatile SingletonTwo INSTANCE=null;
    private SingletonTwo(){
    }
    public static synchronized SingletonTwo getInstance(){
      if(INSTANCE==null){
          synchronized (SingletonTwo.class){
              if (INSTANCE==null){
                  INSTANCE=new SingletonTwo();
              }
          }
      }
      return INSTANCE;
    }
    public Object readResolve(){
        return INSTANCE;
    }
}

注意 :volatile关键字。

1.9并行与并发的区别?(操作系统、多线程)

case1:单核cpu:

          线程实际上是串行(利用时间片,在操作系统中,有一个部分叫任务管理器,它的作用是将cpu时间片【windows下的时间片最小为15ms】分给不同的程序使用,只是由于切换得很快,我们感觉是同时进行得)执行的。

        即:微观串行,宏观并行。

   所以,并发:线程(切换)轮流使用CPU;并发是在同一时间应对多件事情的能力。  

case2:  多核cpu:

          每个核都可以进行调度运行线程,这时候线程可以是并行的。

          并行:是同一时间动手做多件事情的能力。

现在计算机以多核cpu居多,在多核cpu的情况下:

      并发是在同一时间应对多件事情得能力,多个线程轮流使用一个或多个cpu

      并行是同一时间动手做多件事情的能力,例如:4核cpu同时执行4个线程

1.10 程序计数器是什么?(JVM)

参考 1.5

程序计数器:线程是私有的(每一个线程都有一个程序计数器),内部保存的字节码的行号(可以理解为代码的行号,但有区别)。用于记录正在执行的直接骂指令的地址。

应用场景:线程切换

1.11 详细介绍java堆?(JVM)

java中的堆是一个线程共享的区域。主要用来保存对象实例、数组等,当堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError(内存溢出)异常。

所以,因为是线程共享的,所以存在线程安全问题

在java8中,堆=年轻代+老年代

                        年轻代=Eden区+2个大小严格相同的Survivor(幸存者区)区

                        老年代用于保存生命周期长的对象,一般是一些老的对象。

1.12排序算法(数据结构)

1.12.1冒泡排序

代码实现如下:

public class BubbleSort {
    public static void main(String[] args) {
        int[] a={5,9,7,4,1,3,2,8};
       for (int i=0;i<bubble(a).length;i++){
           System.out.println(bubble(a)[i]);
       }
    }
    public static int[] bubble(int[] a){
        for ( int j=0;j<a.length;j++){
            for (int i=0;i<a.length-1;i++){
                if(a[i]>a[i+1]){
                    swap(a,i,i+1);
                }
            }
        }
        return a;
    }


    public static void swap(int[] a,int i,int j){
        int temp=a[j];
        a[j]=a[i];
        a[i]=temp;
    }
}

优化一后的代码如下,减少不必要得冒泡次数:

public class BubbleSort {
    public static void main(String[] args) {
        int[] a={5,9,7,4,1,3,2,8};
       for (int i=0;i<bubble(a).length;i++){
           System.out.println(bubble(a)[i]);
       }
    }
    public static int[] bubble(int[] a){
        for ( int j=0;j<a.length;j++){
            boolean swapped=false;
            for (int i=0;i<a.length-1;i++){
                if(a[i]>a[i+1]){
                    swap(a,i,i+1);
                    swapped=true;
                }
            }
            if (!swapped){
                break;
            }
        }
        return a;
    }


    public static void swap(int[] a,int i,int j){
        int temp=a[j];
        a[j]=a[i];
        a[i]=temp;
    }
}

1.13如何防止死锁?(多线程)

死锁简单来说就是两个或两个以上的线程,在执行过程中去争夺同一个共享资源,从而导致相互等待的现象。如果没有外部干预的情况下,线程就会一直处于阻塞状态,从而无法继续执行;这种一直处于等待阻塞状态的线程,就会成为死锁线程。

如何避免死锁?
只能外部干预,例如:重启程序,杀掉线程,因此,只能在写代码的时候尽量规避死锁的产生。

避免死锁,就是要破坏死锁产生的条件。但互斥条件是无法破坏的,但其他的条件可以通过人为干

预进行破坏。例如:“请求和保持条件”----可以在首次执行时一次性申请所有资源。

“不可抢占条件”------在线程申请其他资源申请不到的情况下,可以主动释放线程占有的资源。

“循环等待条件”-----按序申请资源来防止死锁的产生。

1.14什么是守护线程?(多线程)

   Java中的线程可以分为两种,一种是用户线程,一种是守护线程

   一般情况下,如果不做特别的说明配置(使用.setDaemon(true/false)可以将线程设置为守护/用户线程),默认都是用户线程

   用户线程(User Thread)是系统的工作线程,它会完成这个程序需要完成的业务操作,例如:main线程

   守护线程(Daemon Thread),也可称为服务线程,当程序中没有可服务的线程时会自动离开(假设当系统只剩下守护线程的时候,Java虚拟机会自动退出)。因此,守护线程的优先级比较低,用于为其他的线程等提供服务

守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。

1.15 使用多线程,模拟实现红灯,绿灯,和黄灯的功能红灯--打出5个数字,接着黄灯亮--打印3个数字,接着绿灯亮--打印5个数字(多线程)

1.定义绿灯的任务

public class Green implements Runnable{
    private Light light;

    public Green(Light light) {
        this.light = light;
    }

    @Override
    public void run() {
        while (true){
            try {
                this.light.green();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.定义红灯任务

public class Red implements Runnable{
    private Light light;

    public Red(Light light) {
        this.light = light;
    }
    @Override
    public void run() {
        while (true){
            try {
                this.light.red();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

 3.定义黄灯任务

public class Yellow implements Runnable{
    private Light light;

    public Yellow(Light light) {
        this.light = light;
    }
    @Override
    public void run() {
        while (true){
            try {
                this.light.yellow();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

4.定义实体类和相关执行顺序等细节

public class Light {
    private final Integer RED_STATE = 1;
    private final Integer YELLOW_STATE = 3;
    private final Integer GREEN_STATE = 2;
    //AtomicInteger :JUC支持线程并发安全
    private final AtomicInteger atomicInteger = new AtomicInteger(RED_STATE);
    //默认使用的是非公平锁
    //new ReentrantLock(true):采用公平锁
    private final Lock lock = new ReentrantLock();
    //为三种颜色的线程准备三个执行条件
    private final Condition RED_CONDITION = lock.newCondition();
    private final Condition YELLOW_CONDITION = lock.newCondition();
    private final Condition GREEN_CONDITION = lock.newCondition();

    //红灯亮前提:状态为1

    /**
     * synchronized:java语言级别支持的线程同步方式
     * 可重入锁是Java API级别的线程同步方式
     */
    public  void red() throws InterruptedException {
        //同步方法使用lock对具有线程安全的数据上锁
        try {
            //上锁后,不管程序是否发生异常,都必须要解锁,否则会出现死锁,可重入锁可以锁多次,锁了几次就需要解锁几次
            lock.lock();//利用死循环空转,不会释放线程,直到得到CPU使用权为止
           //if (lock.tryLock()) {//尝试得到CPU使用权,不会空转
                while (this.atomicInteger.get() != RED_STATE) {
                    RED_CONDITION.await();
                }
                for (int i = 1; i <=5; i++) {
                    System.out.println("红灯亮" + i + "次");

                }
                Thread.sleep(1000);
                atomicInteger.set(YELLOW_STATE);
                //只有黄灯线程唤醒
                YELLOW_CONDITION.signalAll();
           // }
        } finally {
            //上锁后,不管程序是否发生异常,都必须要解锁,否则会出现死锁,可重入锁可以锁多次,锁了几次就需要解锁几次
            lock.unlock();
        }


    }

    public  void yellow() throws InterruptedException {
        try {
            lock.lock();
          //  if (lock.tryLock()){
                while (atomicInteger.get() != YELLOW_STATE) {
                    YELLOW_CONDITION.await();
                }

                for (int i = 1; i <=3; i++) {
                    System.out.println("黄灯亮" + i + "次");
                }
                Thread.sleep(1000);
                atomicInteger.set(GREEN_STATE);
                GREEN_CONDITION.signalAll();
           // }



        } finally {
            lock.unlock();
        }


    }

    public void green() throws InterruptedException {

        try {
            lock.lock();
          // if (lock.tryLock()){
               while (this.atomicInteger.get() != GREEN_STATE) {
                   GREEN_CONDITION.await();
               }
               for (int i = 1; i <=5; i++) {
                   System.out.println("绿灯亮" + i + "次");

               }
               Thread.sleep(1000);
               atomicInteger.set(RED_STATE);
               RED_CONDITION.signalAll();
          // }

        } finally {
            lock.unlock();
        }

    }

测试类:

/*
*2、使用多线程,模拟实现红灯,绿灯,和黄灯的功能
红灯--打出5个数字,接着黄灯亮--打印3个数字,接着绿灯亮--打印5个数字
以此循环
使用可重入锁

*
* */
public class Test{
    public static void main(String[] args) {
        Light light=new Light();
        new Thread(new Red(light),"RED").start();
        new Thread(new Green(light),"GREEN").start();
        new Thread(new Yellow(light),"YELLOW").start();
    }
}

1.16java中的wait()和sleep()方法有什么不同?

共同点:

wait()、wait(long)和sleep(long)都能让当前线程暂时放弃CPU的使用权,进入阻塞状态。

不同点:

1.方法归属不同--sleep() 来自 Thread,wait() 来自 Object。

   sleep是Thread的静态方法。

   wait()和wait(long)都是Object的成员方法,每个对象都有。

2.醒来时机不同--sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。

   执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来

   wait(long)和wait()还可以被notify唤醒,wait()如果不唤醒会一直等下去

   它们都可以被打断唤醒

3.锁的特性不同--sleep() 不释放锁;wait() 释放锁。

    wait方法的调用必须先获取wait对象的锁,而sleep无此限制

    wait方法执行后会释放对象锁,允许其它线程获得该对象锁

    sleep方法如果在synchronized代码块中执行,并不会释放对象锁

1.17Java中 Integer和int的区别?

1.Integer是int的包装类,int是java中一种基本的数据类型

2.Integer实际是对象的引用,当new一个Integer时,实际生成一个指针指向对象,而int则直接存储数值

3.Integer的默认值为null,而int的默认值为0.

1.18值传递与引用传递问题

写出如下代码的执行结果:

public class TestStringBuffer {
        public static void main(String args[]) {
            StringBuffer a = new StringBuffer("A");
            StringBuffer b = new StringBuffer("B");
            mb_operate(a, b);
            System.out.println(a + "." + b);
        }
        static void mb_operate(StringBuffer x, StringBuffer y) {
            x.append(y);
            y = x;
        }
    }

答:AB.B

1.19java中线程的状态有哪些?

在Java中,线程的运行状态被定义为6个枚举值,分别是:

1新建状态(NEW):线程已经建好,还没调用start()方法。

2.就绪状态(RUNNABLE):线程可能正在运行,也可能在就绪队列等待系统分配CPU资源。

3.阻塞状态(BLOCKED):线程处于等待(Monitor Lock)锁的状态。

4.等待状态(WAITTING):线程处于条件等待状态,当触发后,会被唤醒。

5.计时等待状态(TIMED_WAIT):与等待状态时一样的,只是比等待状态多了一个超时触发机制。

6.终止状态(TERMINATED):表示线程执行结束。线程的生命周期走到尽头,线程的资源释放。

1.20二叉树展开为链表(算法)

题目如下: 114. 二叉树展开为链表 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public void flatten(TreeNode root) {
        //先序遍历:根左右
        if(root==null) return;
        TreeNode right=root.right;
        root.right=root.left;
        root.left=null;
        flatten(root.right);
        flatten(right);

        TreeNode p=root;
        while(p.right!=null){
            p=p.right;
        }
        p.right=right;
    }
}

1.21 jsp八大隐含对象

jsp八大隐藏对象
类型代表作用
pageContextPageContext当前页面上下文获取页面中的其他的隐含对象,同时它还是一个域对象
exceptionThrowable异常信息获取页面中的异常
requestHttpServletRequest请求可以获取用户发送的请求信息,他也是一个域对象
responseHttpServletResponse响应向浏览器发送响应信息
applicationServcletContext代表整个WEB应用Java Web中最大的域对象
sessionHttpSession当前会话可以作为域的对象来共享数据
outJspWriter输出流可以向页面输出内容
configServlrtConfig当前JSP的配置信息可以获取到servlet标签中初始化参数
pageObject在servlet方法中有如下代码Object page=this代表当前JSP的对象

1.22 编写程序实现1到100万中间素数个数的计算

分析:需要利用多线程对任务进行拆分,不能一个for循环写到底,后者会导致程序运行时间过长。

任务定义和任务拆分代码如下:

import java.util.concurrent.RecursiveTask;

public class IsPrimeCountTask extends RecursiveTask<Integer> {
    /**
     * 要统计的内容
     * */

    private int num;
    /**
     * 统计行开始的索引
     * */
    private int begin;
    /**
     * 设置每个操作行的数量
     * **/
    private int groupNumber= Runtime.getRuntime().availableProcessors();
    private static final int THRESHOLE=10000000;
    private int end;

    public IsPrimeCountTask( int begin, int end) {

        this.begin = begin;
        this.end = end;
    }

    // 判断一个数是否为素数
    public static boolean isPrime(int num) {
        if (num <= 1) {
            return false;
        }

        if (num <= 3) {
            return true;
        }

        if (num % 2 == 0 || num % 3 == 0) {
            return false;
        }

        // 只需要试除到平方根
        for (int i = 5; i * i <= num; i += 6) {
            if (num % i == 0 || num % (i + 2) == 0) {
                return false;
            }
        }

        return true;
    }

    @Override
    protected Integer compute() {
        int sum=0;
        if((end-begin)+1<=THRESHOLE){
            for (int i=begin;i<end;i++){
               // System.out.println("begin=="+begin+" , end=="+end+", 计算i="+i);
                if(isPrime(i)){
                    sum++;
                }
            }
        }
        else {//根据groupNumber进行分组,每一组创建一个wordCountTask
          int middle=(begin+end)/2;
          IsPrimeCountTask task1=new IsPrimeCountTask(begin,middle);
          IsPrimeCountTask task2=new IsPrimeCountTask(begin+1,end);
          task1.fork();
          task2.fork();
          sum=task1.join()+task2.join();



        }

        return sum;
    }

测试类:

public class ForkJoinTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool pool = new ForkJoinPool();

        IsPrimeCountTask isPrimeCountTask=new IsPrimeCountTask(1,1000000);
        ForkJoinTask forkJoinTask = pool.submit(isPrimeCountTask);
        System.out.println(isPrimeCountTask.get());
    }
}

运行结果:

1.23多线程中start()和run()方法的区别是什么?

1.

    start()用来启动线程,改变的是线程的状态,有就绪进入执行。

    run()方法中包含要执行的代码,在执行状态状态时运行里面的代码(run() 方法用于执行线程的运行时代码)。

2.

    run() 可以重复调用。

    start() 只能调用一次。

1.24创建线程有哪几种方式?

创建线程有三种方式:

  • 继承 Thread 重写 run 方法;
  • 实现 Runnable 接口;(推荐)
  • 实现 Callable 接口。(与实现Runnable接口的方式相比有返回值)

1.25H2O的生成(线程的同步)

题目如下:

1117. H2O 生成 - 力扣(LeetCode)

class H2O {

   Semaphore H=new Semaphore(2);
   Semaphore O=new Semaphore(0);
    public H2O() {
        
    }

    public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
		H.acquire(1);
        // releaseHydrogen.run() outputs "H". Do not change or remove this line.
        releaseHydrogen.run();
        O.release(1);
    }

    public void oxygen(Runnable releaseOxygen) throws InterruptedException {
        O.acquire(2);
        // releaseOxygen.run() outputs "O". Do not change or remove this line.
		releaseOxygen.run();
         H.release(2);
    }
}

2.计算机网络

2.1 OSI七层模型

2.2.什么是网络四元组?

四元组:在 TCP协议中,如何去确定一个客户端连接的组成要素。包括:源IP地址目标IP地址源端口号,目标端口号

当一个客户端和服务端建立一个TCP连接是,通过四元组来确定唯一的一个TCP连接。

2.3.TCP的三次握手和四次挥手?

TCP协议是传输层的协议;传输层负责简历端到端的连接。

2.3.1TCP的三次握手

上图解释:主机A用户输入一个字符“C”后,产生一个TCP的段。段的序列号是42,ACK确认号是79。这两个数是建立连接的时候随机选择的。主机B回传这个字符,返回的时候的序列号是79,ACK是43,由于刚才来的那个段例只装了一个字符(刚好占位一个字节),所以,43=42+1,是这么来的。与此同时,我们确认了43之前的字节都已经接收成功。ACK消息就到主机A了。主机A再发一个确认,不带任何数据,这时序列号变成了43。ACK变成了80。利用这个确认刚才收到的消息。

(1) 服务端通过socket,bind和listen准备好接受外来的连接,此时服务端状态为Listen (2)客户端通过调用connect来发起主动连接,导致客户端TCP发送一个SYN(同步)字节,告诉服务器客户将在(待建立的)连接中发送的数据的初始序列号,客户端状态为SYN_SENT。 (3)服务器确认(ACK)客户的SYN,并自己也发送一个SYN,它包含服务器将在同一连接中发送数据的初始序列号。 (4)客户端确认服务的ACK和SYN,向服务器发送ACK,客户端状态ESTABLISHED (5)服务器接收ACK,服务器状态ESABLISHED。

3.操作系统与linux

3.1线程与进程的区别?

当一个程序被运行,从磁盘程序的代码到内存中,这时就开启了一个进程。

一个进程内会有1到多个线程。

区别:

       进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务

       不同的进程使用不同的内存空间,在当前进程下的所有的线程可以共享内存空间。

       线程更轻量,线程的切换成本要比进程的切换成本低

4.数据库相关

4.1数据库事务的隔离级别?

数据库事务隔离级别用于解决数据库并发事务问题。

1.读未提交的数据

2.读已提交的数据

3.可重复读数据

4.串行化(一般不用)

数据库事务的四大隔离级别如下图所示:

            其中,mysql默认的事务隔离级别为Reaptable Read;Oricle默认的事务隔离级别为Read committed,从上到下,数据的安全性增加,但性能减弱。

# 查看事务的隔离级别
SELECT @@TRANSATION_ISAOLATION;


#设置事务的隔离级别,SESSION:当前会话。GLOBAL:所有会话。
SET [ SESSION | GLOBAL ] TRANSACTION ISAOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};

4.2数据库的三大范式

第一范式(1NF)

       确保数据库表字段的原子性(不可再分,数据表中存放的数据准确可靠),同时表必须要有一个主键(一个字段只维护一个信息)。

       缺点:满足第一范式设计的数据表字段(数量会增加)。

第二范式(2NF)

    (首先必须要满足第一范式)非主键列必须完全依赖于主键,而不能只依赖于主键的一部分。(一张数据表的所有列只用于描述一类实体信息)

       缺点:满足第二范式设计的数据表,表的数量会增加。

第三范式(3NF)

       (首先必须满足第二范式)必须有主键,有外键;使用主外约束维持表间关系。消除对主属性的传递依赖。

         缺点:列变多,数据有冗余。

4.3.数据库事务的特性?

ACID保证是原子性(Atomicity)一致性(Consisttency)隔离性(Isolation)耐用性(Durability)的缩写,是对数据库事务方面的一组要求。一般来说,系统对ACID保证越严格,则在性能上做出的让步就会越大。开发人员用ACID分类来交流不同方案所做的妥协,比如在聊NoSQL系统时。

1.原子性:无论成败,事务必须整体执行。

原子性事务不能被部分执行:或者整个操作都执行了,或者数据库保持原样。比如说要删除某个用户的所使用记录,如果作为一个事务的话,或者全删掉,或者一条都不删。最终不能是有些删掉了,有些没删掉,还保持原来的状态。甚至在系统出错或断电后,任然要保持原子性。原子性在这里的意思是不可再分。

2.一致性:始终确保约束条件。

成功完成的事务必须符合系统中定义的所有数据的完整性约束。例如:主键必须唯一、数据必须要符合某种特定的模式,或外键必须要指向所存在的实体。产生不一致状态的事务一般也会失败,然而小问题是可以自动解决的,比如:将数据转换为正确的形态。不要吧一致性的C与CAP定理中的C搞混了,那个C是指在读取分布式存储的数据时,确保呈现的是一个视图。

3.隔离性:并发事务不会相互干扰。

不管是并发还是线性执行,隔离性事务的执行结果应该都是一样的。系统的隔离水平会直接影响它执行并发操作的能力。全局锁是一种比较低幼的隔离方式,由于在事务期间会把整个数据库锁住,所以只能串行处理事务。这是很强的隔离性保证,但效率也极低:那些跟事务完全无关联的数据集根本不应该被锁住(比如说:一个用户添加评论时不应该导致另一个用户无法更新自己的个人资料)。在现实情况中,数据库系统会提供更加精细的和有选择性的锁方式(比如:锁表、锁记录或锁数据域),以实现各种程度的隔离水平。更复杂的系统甚至可能会采用隔离水平最低的锁方式,乐观地并行执行所有事务,直到检测到冲突时才会逐步细化锁模式。

4.耐用性:事务是永久性的。

事务的耐用性是对持久化生效的保证,在重启、断电、系统错误甚至硬件实践的情况下,持久化的效果依然不受影响。比如SQLite内存模式下的事务就没有耐用性,进程退出后所有数据都没了。而在SQLite把数据写到硬盘中时,事物的耐用性就很好,因为机器重启后数据还在。

5.框架相关

5.1Spring中常见的注解有哪些?

5.2Spring MVC中常用注解有哪些?

5.3Spring  Boot中常用注解有哪些?

5.4 Spring MVC的执行流程?

5.5Spring中BeanFactory和FactoryBean的区别?

BeanFactory:以Factory结尾,说明BeanFactory是一个工厂类。负责生产和管理Bean的一个工厂接口,提供一个Spring Ioc容器规范;eanFactory是Spring容器的核心接口,它是用来管理和维护Bean实例的容器;BeanFactory提供了依赖注入、生命周期管理和AOP等功能,它是Spring IoC(控制反转)的基础;BeanFactory的实现类包括XmlBeanFactory、DefaultListableBeanFactory等,其中XmlBeanFactory已经被废弃,推荐使用ApplicationContext作为替代。

FactoryBean:FactoryBean是一个接口,它允许用户自定义Bean的创建过程,通常用于创建复杂的Bean对象;FactoryBean的实现类可以覆盖getObject()方法,该方法负责实际创建和返回Bean实例。(一种Bean创建的一种方式,对Bean的一种扩展。对于复杂的Bean对象初始化创建使用其可封装对象的创建细节,FactoryBean通常用于创建某些特殊类型的Bean,例如数据库连接池、RMI代理、代理工厂等。)

总结BeanFactory是Spring的核心容器,用于管理和维护Bean对象的生命周期,提供了IoC和AOP的支持。FactoryBean是一个接口,允许用户自定义Bean的创建过程(通过工厂模式来生产Bean),通常用于创建特殊类型的Bean。FactoryBean的实现类在Spring中也是以Bean的形式存在,但它们用于封装自定义的创建逻辑。

5.6什么是AOP(Aspect-Oriented Programming)?

(AOP)面向切面编程:将公共逻辑(事务管理、日志、缓存等)封装为切面,和业务代码进行分离,可以减少重复代码和降低模块之间的耦合度。

5.7@Autowired和@Resource的区别?

@Autowired是Spring的注解,默认按照类型匹配(byType)

@Resource是J2ee的注解,默认按照byName模式自动注入,@Resource有两个重要的属性:name和type,name属性指定bean的名字,type属性指定bean的类型,如果使用name属性,则按照byName模式的自动注入策略;如果使用type属性,则按照byType模式的自动注入策略。如果既不指定name也不指定type,spring容器将通过反射技术默认按byName模式注入

司马万
关注 关注
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
22年国内最牛的Java面试八股文合集(全彩版),不接受反驳
Cr1556648487的博客
08-04 1万+
秋收已然来临,找工作的小伙伴比比皆是,很对小伙伴早早的就开始储备技术,准备秋招面试了。为了帮助小伙伴更好的应对面试,我拉来十几个大佬,汇总一线大厂的情况,给你整了一套超全的面试资料: 1658页Java面试突击核心讲包含的知识点也是比较广比较多的:java基础、JVM、多线程、MySQLspringspringbootspringcloud、dubbo、mybatis、redis、网络IO、Linux、MQ、zookeeper、netty、大数据、算法、项目、设计模式等等;...
2022年国内最牛的Java面试八股文合集(MCA版),不接受反驳
m0_69305074的博客
10-18 614
纵观今年的技术招聘市场,!即便遭受 Go等新兴语言不断冲击,依旧岿然不动。,Java程序员的群体也最为庞大,,彼此之间都有更多的选择。换句话说,也是最修罗场的!所以,要想在这个,咱就一定要做好准备,把那些必考点、套路都给吃透了!内容展示:由于需要控制文章幅篇小编呢就用截图的方式给大家展示。
java面试八股文
weixin_45927841的博客
03-21 5万+
目录一、java(1)集合1.list:LinkedList、ArrayList和Vector2.set:HashSet和TreeSet3.map:HashMap、TreeMap和HashTable4.list和set的区别(2)其他1.面向对象三大特性2.Object类的常用方法3.Java中线程安全的基本数据结构4.string、stringBuffer和stringBuilder5.抽象类与接口的区别6.java的基本数据类型7.java代码块执行顺序8.static关键字9.覆盖(重写)和重载的区别
2024年 Java 面试八股文(20w字)
热门推荐
leader_song的博客
08-04 17万+
目录第一章-Java基础篇1、你是怎样理解OOP面向对象 难度系数:⭐2、重载与重写区别 难度系数:⭐3、接口与抽象类的区别 难度系数:⭐4、深拷贝与浅拷贝的理解 难度系数:⭐5、sleep和wait区别 难度系数:⭐6、什么是自动拆装箱 int和Integer有什么区别 难度系数:⭐7、==和equals区别 难度系数:⭐8、String能被继承吗 为什么用final修饰 难度系数:⭐9、String buffer和String builder区别
Java面试八股文合集(持续更新)
qq_51580161的博客
09-27 1万+
我们使用java编译命令就能将java源文件编译对应成字节码文件(.class),字节码文件是一种八位数据的二进制流文件,可以被JVM快速加载到内存中运行。
22 年国内最牛的 Java 面试八股文合集(全彩版),不接受反驳
weixin_70730532的博客
06-30 931
很多小伙伴从四月份就开始准备面试了,截止现在已经过去 2 个多月的时间,显然这段时间的准备没有白费,很多小伙伴都报喜,成功拿到了 XX 公司的 Offer下面我把这段时间给小伙伴本准备的 Java 面试八股文合集(全彩版)拿出来,来帮助没有看过这份笔记的小伙伴们更好的应对面试:这份笔记是我拉来十几个大佬,汇总一线大厂的情况,整理的一套超全的面试资料: 1658 页 Java 面试突击核心讲包含的知识点也是比较广比较多的:java 基础、JVM、多线程、MySQLspringspringboot、spri
这也许是22年国内最牛的Java面试八股文合集(全彩版),不接受反驳
YYniannian的博客
07-28 181
为了帮助小伙伴更好的应对面试,我拉来十几个大佬,汇总一线大厂的情况,给你整了一套超全的面试资料:1658页Java面试突击核心讲包含的知识点也是比较广比较多的:java基础、JVM、多线程、MySQLspringspringbootspringcloud、dubbo、mybatis、redis、网络IO、Linux、MQ、zookeeper、netty、大数据、算法、项目、设计模式等等;刷完这一套高质量题集,下一个金九银十妥妥的~...
国内最牛Java面试八股文合集,刷完你也可以当架构师!
m0_69305074的博客
06-17 524
(含:单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式,适配器模式,装饰器模式,代理模式等23种设计模式...)(含:mysql数据库基础知识,数据类型,失误,试图,mysql索引,mysql锁,mysql优化,等...)(含:Java概述,语法,面向对象,IO流,API,集合,NIO,HashMap,基础常见面试题....)(含:分布式系统原,数据分布式方式,基本副本协议,Lease机制,Dubbo,集群,分布式锁,等...)(含:Dubbo基础,架构设计,集群,配置,通信协议,SPI,等...)
JAVA面试八股文整理
qq_35571432的博客
12-15 2万+
目录Java基础Java多线程Java线程池为什么要用线程池线程池的创建方式线程的生命周期线程的五个生命周期僵死进程 Java基础 Java多线程 Java线程池 为什么要用线程池 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。 提供更多更强大的功能:线程池具备可拓展性,允
Java面试八股文2024最新版
12-19
工作了二年多想跳槽了,自己整理的Java面试八股文 java面试题 2023最新整理 java八股文 高频面试题 里边包含了很多领域的java se基础、springboot springcloud MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached...
2024最强Java面试八股文
01-04
《2024最强Java面试八股文》是一份针对Java面试的全面指南,内容涵盖JVM、MQ、MyBatis、MySQL、Redis、Spring BootSpring Cloud以及设计模式等多个方面。这份资料旨在帮助求职者系统地复习和掌握Java及相关技术的...
Java面试八股文2023最新版
03-14
工作了一年多想跳槽了,自己整理的Java面试八股文 java面试题 2023最新整理 java八股文 高频面试题 里边包含了很多领域的java se基础、springboot springcloud MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached...
2024年Java最全面试八股文合集
02-29
2024年Java最全面试八股文合集,金三银四马上到了,要想升职加薪的兄弟们还不快赶紧卷起来!!
2024最强Java面试八股文过过过
02-26
《2024最强Java面试八股文》是一份针对Java面试的全面指南,内容涵盖JVM、MQ、MyBatis、MySQL、Redis、Spring BootSpring Cloud以及设计模式等多个方面。这份资料旨在帮助求职者系统地复习和掌握Java及相关技术的...
JAVAEE之多线程进阶(2)_ CAS概念、实现原理、ABA问题及解决方案
最新发布
2301_80653026的博客
05-29 1183
CAS全称Compare and swap,字面意思:”比较并交换“,它是一条 CPU 并发原语,用于判断内存中某个值是否为预期值,如果是则更改为新的值,这个过程是原子的。全称 Compare and swap, 即 “比较并交换”. 相当于通过一个原子的操作, 同时完成 “读取内存, 比较是否相等, 修改内存” 这三个步骤. 本质上需要 CPU 指令的支撑。
Spring Bean Map之舞
m0_51176516的博客
05-23 727
Spring框架中,Bean的生命周期管理是其核心功能之一。当Spring容器启动时,它会根据配置信息(如XML配置文件、注解等)来创建和管理Bean实例。这些Bean实例默认会被放置在一个Map数据结构中,以便容器能够快速地根据Bean的名称(或ID)来查找和访问它们。 在Spring的源码中,Bean实例的存储和管理主要依赖于DefaultSingletonBeanRegistry类。这个类内部使用了一个名为singletonObjects的Map来存储单例Bea...
java学习和项目总结
2301_79390585的博客
05-24 870
JRE:JRE是Java 运行时环境,主要包括了两个运行需要的组件:JVM 和 Java 核心类库,如果不进行java开发只想运行就可以只下载JRE。而Java语言,它的代码会先通过javac编译成字节码,再通过jvm将字节码转换成机器码(0和1)执行,即解释运行和编译运行配合使用,所以是混合型。在程序运行之前,通过编译器将源程序编译成机器码可运行的二进制,以后执行这个程序时,就不用再进行编译了。JVM:JVM是java进行编译的虚拟机,是Java 能够跨平台运行的核心。特点:执行速度慢、效率低;
【STM32学习】HAL库点灯学习
2301_78895982的博客
05-26 747
STM32基于HAL库流水灯实验_hel库安装教程中文版-CSDN博客t=N7T8。
java面试八股文 下载
09-06
Java面试八股文是指在准备Java面试时需要重点掌握的一些核心知识和常见问题。下载Java面试八股文是为了将这些知识和问题整理成一份文档,方便学习和准备。这份文档通常包含Java基础知识、Java集合框架、多线程、IO流、数据库、设计模式、网络编程等各个方面的问题和答案。 Java面试八股文的下载对于准备面试的候选人来说非常重要。因为Java面试的问题种类繁多,面试官可能会选择任意一个方面进行提问。同时,面试官一般会倾向于问一些经典的问题,以便评估候选人对于基础和核心知识的掌握程度。如果候选人能够提前下载并学习这份八股文,不仅可以提高回答问题的准确性和流利度,还能够帮助候选人在面试中更好地展示自己的能力和优势。 当然,光有Java面试八股文还远远不够,候选人还需要结合自己在实际项目中的经验、对于面试岗位的理解以及对于技术的持续学习来进行综合准备。另外,面试本身也不是仅仅靠死记硬背答题,更重要的是能够准确理解问题的本质,提出合理的解决思路和方案,并能够清晰地表达自己的观点和思考过程。 总的来说,下载Java面试八股文是为了提高面试准备的效率和质量,但准备八股文只是面试准备的一部分,更重要的是掌握基础知识、理解问题本质,发展自己的解决问题的能力。希望以上回答对您有所帮助。

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • JDBC有关接口介绍 2534
  • SQL关系数据库示例 1571
  • 什么是网络四元组? 1349
  • 《软件工程导论(第六版)》第二章 可行性分析与研究 1219
  • 为什么启动一个线程不直接调用run(),而要调用start()启动? 808

最新评论

  • Java多线程实践2-Thread类与Runable接口

    CSDN-Ada助手: 恭喜您写下了第19篇博客!标题为“Java多线程1”,这个主题非常有意思。您在多线程方面的知识深度令人钦佩,能够将这么复杂的概念用简洁的方式呈现出来。 在您的下一篇博客中,我建议您可以进一步探讨多线程在实际项目中的应用,并分享一些实际案例。这样不仅可以加深读者的理解,还能够帮助大家更好地应用多线程技术。期待您的下一篇博文,谢谢您的分享!

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • Ruo-Yi前后端分离版本相关笔记
  • Spring01--Spring Framework
  • Java基础之“==”和equals的区别
2023年28篇
2022年8篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

深圳SEO优化公司南山网络广告推广哪家好安康seo网站推广木棉湾网站开发报价威海品牌网站设计价格鄂州关键词排名包年推广公司景德镇百度网站优化排名哪家好安顺网络广告推广公司无锡网站优化按天计费多少钱池州网站优化按天扣费报价东营网站设计公司安庆网站推广系统多少钱济源如何制作网站报价茂名营销型网站建设价格钦州建站推荐徐州网站开发哪家好吴忠高端网站设计哪家好伊犁SEO按效果付费报价山南建站中山百度关键词包年推广推荐木棉湾设计公司网站价格郑州网站优化多少钱盐田SEO按天计费公司霍邱网站设计模板多少钱龙岗百度竞价哪家好揭阳网站制作诸城网站制作设计价格大芬百度seo信阳网站优化按天收费报价西乡优化公司南宁seo排名多少钱歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化