当前位置: 首页 > news >正文

网站如何做优化排名吃什么补肾气效果好

网站如何做优化排名,吃什么补肾气效果好,网上商城网站建设规划,wordpress教育培训Java 是一种广泛使用的面向对象编程语言#xff0c;在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性#xff0c;适用于多种应用场景#xff0c;包括企业应用、移动应用、嵌入式系统等。 以下是几个面试技巧#xff1a; 1. 复习核心概念#xff1a;回顾 Ja… Java 是一种广泛使用的面向对象编程语言在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性适用于多种应用场景包括企业应用、移动应用、嵌入式系统等。          以下是几个面试技巧    1. 复习核心概念回顾 Java 的核心概念如面向对象编程、类和对象、继承和多态、异常处理、集合框架等。确保对这些基础知识有清晰的理解。    2. 熟悉常见问题预测并准备常见的面试问题如 什么是 Java 的封装、继承和多态什么是抽象类和接口它们的区别是什么 等。熟悉这些问题的答案以便能够流利、清晰地回答面试官的提问。    3. 编码实践练习编写一些简单的 Java 代码以加深对基础概念的理解。尝试解决一些常见的编程问题如逆序字符串、查找数组中的最大值等。这将有助于您在面试中展示自己的编码能力。    4. 项目经验准备复习您在 Java 开发方面的项目经验。准备一些项目细节和亮点强调您在项目中所承担的角色和技术贡献。面试官通常会关注您的项目经验因此务必能够清晰而有条理地介绍您的项目经历。    5. 注意优缺点在回答问题时尽量不仅停留在正确的答案上还要深入思考并表达特定功能、概念或语言特性的优缺点。面试官通常会更关注您的思考能力和对技术的全面理解。    6. 积极参与在面试中积极与面试官互动。表达自己的观点和思考提出问题或寻求澄清。这不仅能展示您的积极性和好奇心还能促进面试的互动和对话。    7. 准备好问题在面试结束时通常会给您提供机会提问。为了展示您对岗位和公司的兴趣准备一些相关问题例如关于公司文化、技术栈、团队合作等方面的问题。          最重要的是保持自信和冷静。提前准备并对自己的知识和经验有自信这样您就能在面试中展现出最佳的表现。祝您面试顺利 目录 一、Java Object类有哪些方法分别作用 二、HashMap原理是否存在线程安全问题 三、Java如何进⾏线程同步 四、CAS原理 五、JVM垃圾回收之GC算法有哪些 六、Mysql索引原理以及查询优化 七、TCP拥塞控制 八、算法  给定—棵二叉树找到这棵树最中最后—行中最左边的值 九、知道什么设计模式分别介绍 十、算法求⽆序数组中第k⼤的数 十一、算法求旋转数组找最⼩值⼆分法 十二、算法判断⼆叉树是否镜像递归 十三、如何理解前后端分离 十四、有哪些后端开发经验做了什么 十五、介绍HashMap与TreeMap区别 十六、⽤HashMap实现⼀个有过期功能的缓存 十七、平时怎么学习新知识 十八、最近看了什么书 一面题 一、Java Object类有哪些方法分别作用 Java中的Object类是所有类的超类父类任何类都直接或间接地继承自Object类。因此Object类中的方法对所有Java对象都是可用的。下面是一些最常用的Object类方法及其作用  1. public boolean equals(Object obj) 检查调用该方法的对象是否等于参数传递的对象。默认实现是比较两个对象的内存地址即它们是否为同一对象但很多类重写此方法以提供有意义的相等性比较。Object中的equals方法比较的是对象的地址是否相同  equals方法可以被重写重写后equals方法比较的是两个对象值是否相同。在Java规范中对equals方法的使用必须遵循以下几个规则 自反性对于任何非空引用值XX.equals(X)都应返回true对称性对于任何非空引用值X和Y当且仅当 Y.equals(X)返回true时 X.equals(Y)也应该返回true传递性对于任何非空引用值XYZ如果X.equals(Y)返回true并且Y.equals(Z)返回true那么X.equals(Z)应返回true一致性对于任何非空引用值X和Y多次调用 X.equals(Y)始终返回true或始终返回false。equals和 的区别 equals比较的是两个对象值是否相等如果没有被重写比较的是对象的引用地址是否相同 用于比较基本数据类型的值是否相等或比较两个对象的引用地址是否相等 String  hello    new  String(hello); String  hello1    new  String(hello); System.out .println(hello.equals(hello1)); //重写了 ,比较的是值 ,输出结果为true System.out .println(hello  hello1); //比较的是引用地址 ,输出结果为false int  age    10; int  age2    10; //比较基本类型的值 System.out.println(age    age2);       //输出为true  2. public int hashCode() 返回调用对象的哈希码值。默认情况下这个方法返回对象的内存地址转换成的整数值。重写equals()时通常也需要重写hashCode()以便保持equals()为true的两个对象具有相同的哈希码。  3. public String toString() 返回对象的字符串表示形式。Object类的默认实现返回一个由类名符号“”以及对象哈希码的无符号十六进制表示组成的字符串。通常类会重写这个方法提供更有意义的信息。比如System.out.print(person)等价于System.out.print(person.toString());     //默认返回对象的地址 getClass().getName是返回对象的全类名  Integer.toHexString(hashCode())是以16进制无符号整数形式返回此哈希码的字符串表示 形式。  4. public final native Class? getClass() 返回运行时类的Class对象。Class类的实例表示正在运行的Java应用程序中的类和接口。  5. protected native Object clone() throws CloneNotSupportedException 创建并返回调用该方法的对象的一个副本。对象的类必须实现Cloneable接口否则抛出CloneNotSupportedException。 clone生成的新对象与原对象的关系区别在于两个对象间是否存在相同的引用或对应的内存地址是否存在共用情况若存在则为 “浅复 制” 否则为 “深复制” “深复制”时需要将共同关联的引用也复制完全。  6. public void finalize() 当垃圾收集器确定不存在对该对象的更多引用时由垃圾收集器在垃圾回收前调用此方法。子类可以重写finalize()以进行清理工作诸如释放资源等。  7. protected void finalize() throws Throwable 虽然标记为protected但这是对finalize()方法的解释。从Java 9开始已经不再推荐使用finalize()方法取而代之的是使用Cleaner和PhantomReference这样的替代方案。  8. public final void wait() throws InterruptedException 导致当前线程等待直到另一个线程调用此对象的notify()方法或notifyAll()方法。调用wait方法的当前线程一定要拥有对象的监视器锁。wait方法会把当前线程放在对应的等待队列中在这个对象上的所有同步请求都不会得到响应。线程调用将不会调用线程线程一直处于休眠状态。要注意的是  wait方法把当前线程放置到这个对象的等待队列中解锁也仅仅是在这个对象上当前线程在等待过程中仍然持有其他对象的锁。如果当前线程被其他线程在当前线程等待之前或正在等待时调用了interrupt()中断了那么就会抛出InterruptException异常。为什么wait方法一般要写在while循环里 在某个线程调用notify到等待线程被唤醒的过程中有可能出现另一个线程得到了锁并修改了条件使得条件不再满足只有某些等待 线程的条件满足了但通知线程调用了notifyAll有可能出现“伪唤醒”。wait方法和sleep方法的区别 wait方法属于object类当调用wait方法时线程会放弃对象锁进入等待此对象的等待锁定池只有针对此对象调用notify方法后 本线程才会进入对象锁定池准备获取对象锁进入运行状态。sleep方法属于thread类sleep方法导致程序暂停执行指定的时间让出CPU给其他线程但是它的监控状态依然保持当指定的时间到了又会恢复运行状态。在调用sleep方法过程中线程不会释放对象锁。  9. public final native void notify() 唤醒正在等待对象监视器的单个线程。 唤醒可能等待该对象的对象锁的其他线程。由JVM与优先级无关随机挑选一个处于wait状态的线程。 在调用notify()之前线程必须获取该对象的对象锁执行完notify()方法后不会马上释放锁直到退出synchronized代码块当前线程 才会释放锁  notify一次只能随机通知一个线程进行唤醒。  10. public final native void notifyAll() 唤醒正在等待对象监视器的所有线程。 使所有正在等待池中等待同一个共享资源的全部线程从等待状态退出进入可运行状态让它们同时竞争对象锁只有获得锁的线程才能进 入就绪状态。  11. public final void wait(long timeout) throws InterruptedException 使当前线程等待指定的毫秒数除非另一个线程调用notify()或notifyAll()或当前线程被中断。使用该方法时传入的timeout参数是最大等待时间。如果timeout为0则一直等待直到被通知或中断。  12. public final void wait(long timeout, int nanos) throws InterruptedException 使当前线程等待至多timeout毫秒加nanos纳秒除非另一个线程调用notify()或notifyAll()或当前线程被中断。这个方法允许更精细的控制等待的时间。         注意在使用wait()、notify()和notifyAll()这几个方法时必须在同步块或同步方法中调用这是因为它们需要锁定对象监视器。 虽然Object类提供了这些基本方法通常在实际开发中会通过各种并发工具类如java.util.concurrent包中的类来处理线程同步和通知问题因为它们提供了更加高级、易于使用和更可靠的并发管理功能。 最后需要提醒的是Object类的某些方法如finalize()已被标记为过时因为它可能会导致程序性能问题并且不保证垃圾收集器会按时调用它。从Java 9开始finalize()方法被明确标记为过时deprecated并推荐使用其他资源释放机制如try-with-resources语句来管理资源自动关闭。 二、HashMap原理是否存在线程安全问题 HashMap 是 Java 中一种基于哈希表的 Map 接口的实现。它存储的内容是键值对 (key-value 对)每个键映射到一个值。HashMap 允许使用 null 值和 null 键。         HashMap简单说就是它根据键的hashCode值存储数据⼤多数情况下可以直接定位到它的值因⽽具有很快的访问速 度但遍历顺序却是不确定的。         HashMap基于哈希表底层结构由数组来实现添加到集合中的元素以“key--value”形式保存到数组中在数组中key- -value被包装成⼀个实体来处理---也就是上⾯Map接⼝中的Entry。         在HashMap中 Entry[]保存了集合中所有的键值对当我们需要快速存储、获取、删除集合中的元素时   HashMap会 根据hash算法来获得“键值对”在数组中存在的位置以来实现对应的操作⽅法。         HashMap底层是采⽤数组来维护的.Entry静态内部类的数组 static class NodeK,V implements Map.EntryK,V {final int hash;final K key;V value;NodeK,V next;Node(int hash, K key, V value, NodeK,V next) {this.hash hash;this.key key;this.value value;this.next next;}public final K getKey() { return key; }public final V getValue() { return value; }public final String toString() { return key value; }public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}public final V setValue(V newValue) {V oldValue value;value newValue;return oldValue;}public final boolean equals(Object o) {if (o this)return true;if (o instanceof Map.Entry) {Map.Entry?,? e (Map.Entry?,?)o;if (Objects.equals(key, e.getKey()) Objects.equals(value, e.getValue()))return true;}return false;}}         HashMap添加元素将准备增加到map中的对象与该位置上的对象进⾏⽐较(equals⽅法),如果相同,那么就将该位置上 的那个对象(Entry类型)的value值替换掉,否则沿着该Entry的链继续重复上述过程,如果到链的最后任然没有找到与此对象相同的对  象,那么这个时候就会被增加到数组中,将数组中该位置上的那个Entry对象链到该对象的后⾯(先hashcode计算位置如果找到相同 位置便替换值找不到则重复hashcode计算直到最后在添加到hashmap最后⾯  )          HashMap是基于哈希表的Map接⼝的⾮同步实现允许null键值但不保证映射的顺序 底层使⽤数组实现数组中的 每项是⼀个链表 存储时根据key的hash算法来决定其存储位置数组扩容需要重新计算扩容后每个元素在数组中的位置  很耗性能         ConcurrentHashMap是HashMap的线程安全实现允许多个修改操作同时进⾏(使⽤了锁分离技术)它使⽤了多个锁来 控制对hash表的不同段进⾏的修改每个段其实就是⼀个⼩的hashtable它们有⾃⼰的锁。使⽤了多个⼦hash表(段 Segment)允许多个读操作并发进⾏读操作并不需要锁因为它的HashEntry⼏乎是不可变的          这是 HashMap 的一些主要原理和工作方式 存储结构HashMap 在内部使用一个数组来存储数据这个数组又被称为“桶”Bucket。每个桶是一个链表链表的每一个节点是一个 Entry 对象该对象包含键、值以及指向下一个 Entry 节点的引用。 哈希函数 当我们向 HashMap 中插入一个 key-value 对时它首先会使用哈希函数计算出键对象的哈希码。HashMap 通过使用 key.hashCode() 方法来获取哈希码然后通过内部的哈希函数来转换成数组索引。 冲突解决 由于桶的数量有限会发生不同键的哈希码产生相同数组索引的情况这称为“哈希冲突”。HashMap 使用链表来解决冲突所有哈希值相同的元素会被存储在同一个桶的链表中。从 Java 8 开始当同一个桶中的元素个数超过一定的阈值默认是链表长度大于 8链表会被转换成红黑树以提高性能。 查找元素 在需要获取元素时HashMap 使用键对象的哈希码来找到其在数组中的桶位然后遍历链表或红黑树如果转换成红黑树的话来找到对应的节点。 扩容 当 HashMap 中的元素数量达到数组大小和加载因子load factor默认是 0.75的乘积时HashMap 会进行扩容操作即创建一个新的更大的数组并将旧数组中的所有元素重新插入到新数组中。这个过程叫做“rehash”。 迭代HashMap 的迭代器Iterator遍历时按照哈希桶的顺序进行而不是按照键或值的排序顺序。若在迭代过程中对 HashMap 结构进行修改很可能会抛出 ConcurrentModificationException快速失败行为。 HashMap 的这些特性使它成为一个在大多数情况下都有良好性能的键值存储结构。但是正确地了解和使用 HashMap 的原理对于避免性能问题和正确地进行内存使用仍然非常重要。          由于 HashMap 是非线程安全的当多个线程同时对其进行修改时可能会出现几种问题。这些问题不只限制于数据的不一致性还可能引发程序的崩溃。以下是一些可能出现的具体问题 数据丢失 当两个线程同时执行 put 操作它们可能计算出相同的存储位置从而覆盖对方的数据这将导致其中一个键值对丢失。 无限循环 在 JDK 7 及之前版本的 HashMap 中多线程环境下扩容rehashing可以导致循环链表的出现这会导致 get 方法陷入无限循环。 数据不一致 如果一个线程正在读取而另一个线程同时修改了数据结构读线程可能会看到部分更新的数据从而导致不可预料的结果。 ConcurrentModificationException 异常 当一个线程迭代 HashMap 时如果另外一个线程修改了 HashMap 的结构添加或删除任何元素那么迭代器将快速失败并抛出 ConcurrentModificationException。 内存泄漏 在并发环境下由于线程修改的不同步可能导致某些 Entry 节点从未正确删除致使垃圾收集器无法回收这部分内存随之产生内存泄漏。 如果需要在多线程环境下使用 Map 结构而又不想处理上述问题可以使用一些线程安全的替代方案例如 Collections.synchronizedMap(new HashMap()使用 Collections 工具为 HashMap 提供同步的包装器但是每个方法调用都是同步的可能会导致不必要的性能损耗。ConcurrentHashMap一种线程安全且高效的 HashMap 替代实现。它利用分锁机制提供更高的并发性通常是多线程环境下 HashMap 的最佳选择。 了解问题和可能的解决方案可以确保在多线程环境中有效地使用 HashMap避免竞态条件和其他同步相关问题。 三、Java如何进⾏线程同步 在 Java 中线程同步是指多个线程访问共享资源时确保每个线程看到一致的内存状态且不会相互干扰的机制。为了防止线程间出现冲突Java 提供了多种线程同步的机制。 同步方法 (Synchronized Methods) 在方法声明中加入 synchronized 关键字可以使该方法在同一时间内只能被一个线程访问。当一个线程访问一个对象的 synchronized 方法时其他试图访问该对象的 synchronized 方法的线程将会阻塞。 public synchronized void method() {// 同步代码 } 同步块 (Synchronized Blocks) 如果只有方法中的某个代码块需要同步可以使用 synchronized 关键字来同步一个代码块这比同步整个方法更加细粒度可以减少等待时间从而提高性能。 public void method() {// ... 非同步代码synchronized(this) { // this 是锁定的对象也可以是其他对象// 同步代码}// ... 非同步代码 } 锁 (Locks) Java java.util.concurrent.locks 包提供了更加灵活的锁定机制。其中 ReentrantLock 是常用的实现它具有与使用关键字 synchronized 类似的基本行为和语义但拥有额外的功能。 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class Counter {private final Lock lock new ReentrantLock();private int count 0;public void increment() {lock.lock();try {count;} finally {// 确保在发生异常时锁能被释放lock.unlock();}} } 原子变量java.util.concurrent.atomic 包提供了一组原子类例如 AtomicInteger, AtomicLong, AtomicReference 等。这些类利用底层硬件的原子指令来实现同步而无需使用传统的锁机制。这些操作是非阻塞的并且往往是性能更高的选项。 import java.util.concurrent.atomic.AtomicInteger;public class Counter {private AtomicInteger count new AtomicInteger(0);public void increment() {count.incrementAndGet();}public int getValue() {return count.get();} } volatile 关键字 当一个变量被声明为 volatile那么线程每次读取变量时都会从内存中读每次写回变量时都会写入到内存中。这确保了该变量的可见性即当一个线程更新了该变量时其他线程能立刻看到修改后的值。 public class SharedObject {private volatile int sharedVariable 0;public void increment() {sharedVariable;}public int getSharedVariable() {return sharedVariable;} }         请注意volatile 不能保证复合操作如自增的原子性它仅保证变量的读写操作的可见性。 使用哪种同步机制取决于具体的情况正确的选择可以提供良好的性能同时确保线程安全。对于一些高级的同步需要我们可能还需要使用到 Semaphore, CountDownLatch, CyclicBarrier, Phaser 等同步工具它们在 java.util.concurrent 包下提供了更为复杂的并发控制。 四、CAS原理 CASCompare-And-Swap 或 Compare-And-Set是一种用于实现多线程同步的技术。它涉及对内存中的某些值进行原子地比较和更新。原子指的是操作作为一个不可分割的单元执行要么完全执行要么完全不执行不会出现中间状态。 CAS 操作通常包括以下三个操作数 内存位置V要操作的内存地址。期望原值A期望读到的值。新值B若期望原值验证为真即内存位置的值与期望原值相等则更新为此新值。 这个操作的伪代码可以表示为 if memory_value expected_valuememory_value new_value elsehandle the failure (do nothing or retry or abort, etc.) 执行 CAS 操作的基本步骤是 系统从内存地址 V 读取当前值。检查当前值是否与期望原值 A 相等。如果相等则将新值 B 更新到内存地址 V。如果不等则不做任何操作或者采取其他补救措施比如重试或回滚。 CAS 操作是无锁编程中常用的技术有助于在不使用传统锁机制的情况下实现对共享数据的安全操作。在多处理器系统中CAS 是通过硬件指令直接支持的因此可以高效地执行。 CAS 有以下优点 性能由于不需要锁定CAS 可以减少线程上下文切换的成本通常比使用锁更高效。死锁避免既然没有使用传统的锁也就不可能出现死锁的情况。 不过CAS 也存在一些问题 ABA 问题如果值从 A 变成 B然后又变回 ACAS 会认为什么都没有改变然而实际上可能发生了重要的变动。循环时间长如果很多线程同时尝试进行 CAS那么只有一个能成功其他线程可能不得不多次尝试。只能保证一个共享变量的原子操作如果需要同时更新多个共享变量则不能直接使用 CAS。 总的来说CAS 是一种基于硬件实现的轻量级同步机制适用于某些不需要严格的锁定机制就能解决的线程安全问题。它是 Java 中 java.util.concurrent.atomic 包中的原子变量类的关键实现技术。 五、JVM垃圾回收之GC算法有哪些 Java 虚拟机JVM使用垃圾回收GC算法来管理和回收不再使用的内存。主要的垃圾回收算法包括 标记-清除Mark-Sweep算法 标记该阶段标记出所有从根集合开始可达的对象。清除回收所有未被标记的对象占用的内存。 缺点是两个主要方面标记和清除过程的效率不高以及清除后容易产生内存碎片。 复制Copy or Scavenge算法 将内存分为两个相等的区域每次只使用其中的一个。当进行垃圾回收时将正在使用的内存区域中存活的对象复制到未使用的区域然后清除正在使用的内存区域中的所有对象。优点是避免了内存碎片缺点是内存利用效率为 50%。 标记-整理Mark-Compact算法 结合了“标记-清除”和“复制”算法的优点。标记和标记-清除算法一样进行标记。整理将所有存活的对象压缩到内存的一端清理掉端边界外的内存。 优点是解决内存碎片问题而不需要牺牲过多的内存。 增量Incremental垃圾回收 让垃圾回收分批进行不是一次性清理完所有垃圾。目的是减少每次垃圾回收的时间使得垃圾回收对系统的影响更为平滑。 分代收集Generational Collection算法 根据对象存活周期的不同将内存划分为新生代Young Generation、老年代Old Generation和永久代Permanent Generation但在 Java 8 中已经被移除改成了元空间MetaSpace。新生代使用复制算法老年代一般采用标记-清除或标记-整理算法。 并行Parallel垃圾回收 使用多个垃圾回收线程并行执行垃圾回收以提高垃圾回收的效率。适用于多核处理器能够更高效地进行垃圾回收。 并发Concurrent垃圾回收 允许垃圾回收线程与应用程序线程同时工作。旨在减少应用程序的停顿时间适合需要响应时间快的应用。 JVM 中有不同的垃圾回收器实现上述算法。常见的垃圾回收器有 Serial GC单线程运行采用标记-复制算法适合单核处理器或小内存环境。Parallel GC (也称为吞吐量收集器)多线程执行侧重增加吞吐量。CMS (Concurrent Mark-Sweep) GC以获取最短停顿时间为目标使用并发标记清除算法。G1 (Garbage-First) GC旨在为多核机器提供高吞吐量和低延迟的性能表现适用于大内存。 与这些收集器相关的具体细节和性能特征取决于 JVM 的实现和版本。在 OpenJDK 和 Oracle JDK 9 及以后的版本中还提供了新的垃圾回收器如 ZGC 和 Shenandoah旨在降低停顿时间同时适应大内存和多处理器环境。 六、Mysql索引原理以及查询优化 MySQL索引是帮助MySQL高效获取数据的数据结构。以下是一些基本的MySQL索引类型和它们的工作原理 BTree索引最常用的索引类型适合用于全键值、键值范围或键值前缀查找。B-Tree索引能够加速数据的读取但会额外消耗一些空间来存储索引并且在插入、更新和删除时需要同步更新索引。 哈希索引基于哈希表的实现适合等值查询不适合范围查询和部分键匹配查询。主要优点是查找速度快缺点是不支持排序和分组。 全文索引 (Fulltext Indexes)特别适用于对文本内容进行搜索的应用可以快速定位包含某个词或短语的记录。 空间数据索引 (R-Tree)用于空间数据类型如GIS数据。 工作原理 BTree索引通过维护一个平衡搜索树来进行优化检索数据按照顺序存储可用于查找、排序和分组操作。 查询优化 选择正确的索引了解不同类型的查询和哪些情况下最适合使用索引。找出查询中常用的列并为它们建立索引。索引列上进行操作避免在索引列上做运算或使用函数这将导致无法使用索引。使用最左前缀法则对于复合索引确保查询条件与索引中列的顺序一致。避免全表扫描尽量使用索引来避免全表扫描适当地限制查询返回的行数。索引的选择性选择性是不重复的索引值与数据表中的记录数的比例选择性越高的索引性能越好。使用Explain来分析查询使用EXPLAIN语句来分析你的查询以及索引使用情况可以帮助发现性能瓶颈。适当的Like语句当必须使用LIKE查询时如果模式以通配符开头索引将不会被使用。尽可能避免%value%这种模式而使用value%。         确保定期维护索引以防止索引碎片导致查询效率下降。通过定期运行OPTIMIZE TABLE命令可以帮助重新组织存储和重新构建数据库索引进而提高查询效率。 七、TCP拥塞控制 TCP拥塞控制是一种网络机制它旨在防止过多的数据同时注入网络从而避免网络中的路由器或链路过载导致过度的延迟或丢包。TCP的拥塞控制通过动态调整各TCP连接的发送窗口大小来控制它们各自的发送速率。 以下是TCP拥塞控制的主要算法  1. 慢启动Slow Start TCP连接开始时拥塞窗口cwnd从一个很小的值通常是一个MSS即最大报文段大小开始增长每当收到一个ACKcwnd就增加一个MSS这样cwnd每个RTT往返时间就翻倍增长呈指数增长。当cwnd达到慢启动阈值ssthresh时TCP切换到拥塞避免算法。  2. 拥塞避免Congestion Avoidance 这个阶段TCP转变为更稳健的增长方式每个RTT只增加一个MSS呈线性增长以稳步增加网络负载。如果出现丢包如超时或接收到重复ACKsTCP认为网络出现拥塞并且将ssthresh设置为当前cwnd的一半并进入快速恢复或慢启动。  3. 快速重传Fast Retransmit 如果发送端收到三个重复的ACK它就知道该段后面的报文段一定是丢失了而不是需要等待定时器超时。它会立即重传这个失序的报文段而不是等待超时同时不减小cwnd的大小以避免降低传输速率。  4. 快速恢复Fast Recovery 在执行快速重传之后TCP进入快速恢复阶段ssthresh被设置为当前cwnd的一半cwnd被设置为ssthresh加3倍MSS对于之前接收的三个重复ACK然后每接收一个重复的ACK就增加一个MSS。当终于收到了一个新的ACK认为网络恢复了cwnd被重新设置为ssthresh的值TCP进入拥塞避免阶段。  5. 超时处理 如果等待ACK的时间超过了预定的超时时间TCP认为发生了严重的拥塞并将ssthresh设置为当前cwnd的一半并且将cwnd重新设置为1MSS然后进入慢启动阶段。         随着时间的推移TCP的拥塞控制算法也不断发展和完善例如引入了更为复杂的算法如BBRBottleneck Bandwidth and RTT等旨在进一步优化性能。         TCP拥塞控制的工作原理是一个动态的过程它需要根据网络的实时状态不断地调整发送速率。这些机制使得TCP能够动态适应不同的网络拥塞情况能够在各种类型的网络中有效地传输数据同时又不会因为过多的数据流量而导致网络崩溃。 八、算法  给定—棵二叉树找到这棵树最中最后—行中最左边的值 为了在Java中实现这个算法您可以创建一个TreeNode类表示二叉树节点然后使用广度优先搜索BFS遍历整棵树。通过队列来实现BFS算法您可以轻松地按层遍历二叉树并记录下每层的第一个节点。         以下是一个实现这一功能的Java代码示例 import java.util.Queue; import java.util.LinkedList;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val x;} }public class BinaryTreeBottomLeftValue {public int findBottomLeftValue(TreeNode root) {if (root null) return -1;QueueTreeNode queue new LinkedList();queue.add(root);int result root.val; // 初始化结果为根节点的值while (!queue.isEmpty()) {int size queue.size(); // 当前层的节点数量for (int i 0; i size; i) {TreeNode node queue.poll();// 如果是当前层的第一个节点更新结果值if (i 0) result node.val;// 添加子节点到队列if (node.left ! null) queue.add(node.left);if (node.right ! null) queue.add(node.right);}}return result; // 返回最后一行最左边的值}public static void main(String[] args) {// 示例二叉树// 1// / \// 2 3// / / \// 4 5 6// /// 7// 示例二叉树构建TreeNode root new TreeNode(1);root.left new TreeNode(2);root.right new TreeNode(3);root.left.left new TreeNode(4);root.right.left new TreeNode(5);root.right.right new TreeNode(6);root.right.left.left new TreeNode(7);BinaryTreeBottomLeftValue solution new BinaryTreeBottomLeftValue();int bottomLeft solution.findBottomLeftValue(root);System.out.println(Bottom left value: bottomLeft); // 应输出 7} } 在这个Java程序中我们定义了TreeNode类来表示树的节点然后在BinaryTreeBottomLeftValue类中添加了findBottomLeftValue方法。这个方法使用队列来追踪需要访问的节点遍历所有层并记录下每一层中的第一个节点值。最终返回的就是最后一行中最左边的值。 在main方法中我们建立了一个示例二叉树并调用findBottomLeftValue方法来获取最后一行最左边的值。 二面题考察代码能力。60分钟 九、知道什么设计模式分别介绍 设计模式是解决软件设计中常见问题的通用、可重用的解决方案。设计模式可以分为三个主要类别创建型、结构型和行为型。下面是每一类中一些常用的设计模式及其简要介绍          创建型模式 创建型模式专注于如何创建对象或类的实例。 工厂方法模式Factory Method: 允许接口定义创建对象的方法但由子类决定要实例化的类的类型。工厂方法将对象的创建延迟到子类。 抽象工厂模式Abstract Factory: 提供一个接口用于创建相关或依赖对象的家族而不需要明确指定具体类。 建造者模式Builder: 分离复杂对象的构建和表示同样的构建过程可以创建不同的表示。 原型模式Prototype: 通过拷贝一个现有对象的方式来创建对象而不是通过实例化。 单例模式Singleton: 确保一个类仅有一个实例并提供一个全局访问点。          结构型模式 结构型模式与类和对象的组织有关它们定义了类之间的关系来实现更大的功能。 适配器模式Adapter: 允许将不兼容的接口转换为其他类可以工作的接口。 桥接模式Bridge: 分离抽象部分和实现部分使它们可以独立变化。 组合模式Composite: 允许将对象组成树形结构来表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。 装饰器模式Decorator: 向一个现有的对象添加新的功能同时又不改变其结构。 外观模式Facade: 提供了一个统一的接口用来访问子系统中的一群接口。外观定义了一个高层接口让子系统更容易使用。 享元模式Flyweight: 运用共享技术有效地支持大量细粒度的对象。 代理模式Proxy: 为其他对象提供一种代理以控制对这个对象的访问。          行为型模式 行为型模式专注于算法和对象间职责的分配。 责任链模式Chain of Responsibility: 为请求创建一个接收者对象的链。这种模式给更多的对象一个机会处理请求。 命令模式Command: 将请求封装为一个对象从而允许我们使用不同的请求、队列请求、记录日志等来参数化其他对象。 解释器模式Interpreter: 定义一种语法用于一个某个特定类型的问题并提供该语法的解释器。 迭代器模式Iterator: 提供一种方法顺序访问一个聚合对象中各个元素而又不暴露该对象的内部表示。 中介者模式Mediator: 封装多个对象之间复杂的交互和协作关系中介者通过减少类之间的通信线条数量来减少依赖性。 备忘录模式Memento: 在不破坏封装的前提下捕获并保存一个对象的内部状态以便在以后可以恢复到这个状态。 观察者模式Observer: 一种订阅机制当一个对象状态变化时所有依赖它的对象都会收到通知并自动更新。 状态模式State: 允许一个对象在其内部状态改变时改变它的行为。 策略模式Strategy: 定义一系列算法并将每一个算法封装起来使他们可以互相替换。 模板方法模式Template Method: 定义算法的框架允许子类在不改变算法结构的情况下重写算法的某些步骤。 访问者模式Visitor: 对于一个对象结构中的元素允许一个外部的访问者来访问无需改变对象结构的具体元素类。 这些设计模式在软件开发中被广泛应用能够帮助开发者通过事先定义好的、经过实战考验的方法来解决各种设计问题从而编写出更加可维护、更加清晰、更加可靠的代码。 详解设计模式专栏http://t.csdnimg.cn/bzp55后续慢慢更新 十、算法求⽆序数组中第k⼤的数 要在无序数组中找到第k大的数可以使用快速选择Quickselect算法。该算法的思想是基于快速排序的分治思想但是只执行必要的分区操作从而更高效地找到第k大的数。         下面是使用Java实现快速选择算法来找到无序数组中第k大的数的示例代码 import java.util.Random;public class QuickSelect {private static Random random new Random();public static int findKthLargest(int[] nums, int k) {return quickSelect(nums, 0, nums.length - 1, nums.length - k);}private static int quickSelect(int[] nums, int start, int end, int k) {if (start end) {return nums[start];}int pivotIndex partition(nums, start, end);if (pivotIndex k) {return nums[pivotIndex];} else if (pivotIndex k) {return quickSelect(nums, pivotIndex 1, end, k);} else {return quickSelect(nums, start, pivotIndex - 1, k);}}private static int partition(int[] nums, int start, int end) {// 随机选择一个pivotint randomIndex start random.nextInt(end - start 1);swap(nums, randomIndex, end);int pivot nums[end];int pivotIndex start;for (int i start; i end; i) {if (nums[i] pivot) {swap(nums, i, pivotIndex);pivotIndex;}}swap(nums, pivotIndex, end);return pivotIndex;}private static void swap(int[] nums, int i, int j) {int temp nums[i];nums[i] nums[j];nums[j] temp;}public static void main(String[] args) {int[] nums {3, 5, 2, 1, 6, 4};int k 2;int result findKthLargest(nums, k);System.out.println(第 k 大的数是: result);} }         在示例代码中findKthLargest方法接受一个无序数组和一个整数k作为输入返回第k大的数。quickSelect方法是实现快速选择的递归函数用于分区和选择。partition方法根据选定的pivot元素将数组分成两部分并返回pivot的索引。         在示例代码的main方法中我们提供了一个无序数组和一个k值进行测试并打印出结果。         需要注意的是上述代码的示例是找无序数组中第k大的数。如果要找第k小的数只需修改递归调用的条件和相应返回的数值即可。         快速选择算法的时间复杂度为O(N)其中N是数组的长度。 十一、算法求旋转数组找最⼩值⼆分法 求解旋转数组中最小值的问题可以使用二分法进行高效求解。旋转数组是指将一个升序排列的数组的前若干个元素搬到数组末尾而得到的数组。         下面是使用Java实现二分法求解旋转数组中最小值的示例代码 public class MinimumInRotatedArray {public static int findMin(int[] nums) {int left 0;int right nums.length - 1;while (left right) {int mid left (right - left) / 2;if (nums[mid] nums[right]) {left mid 1;} else if (nums[mid] nums[right]) {right mid;} else {right--;}}return nums[left];}public static void main(String[] args) {int[] nums {4, 5, 6, 7, 0, 1, 2};int min findMin(nums);System.out.println(旋转数组中的最小值是: min);} }         在示例代码中findMin方法接受一个旋转数组作为输入并使用二分法来搜索旋转数组中的最小值。算法首先初始化左右两个指针指向数组的第一个和最后一个元素。然后通过计算中间元素的索引将问题的规模缩小为子数组。在每一次迭代中比较中间元素和最右侧元素的大小关系根据比较结果调整左右指针的位置直到最小值被找到。         在示例代码的main方法中我们提供了一个旋转数组进行测试并打印出最小值。         该算法的时间复杂度为O(logN)其中N是旋转数组的长度。二分法的每一次迭代都将问题的规模减半因此算法的时间复杂度是对数级别的。 十二、算法判断⼆叉树是否镜像递归 判断二叉树是否镜像可以使用递归的方式来解决。对于两棵树是镜像的意味着它们的根节点的值相同并且每个树的右子树都与另一个树的左子树镜像对称。         下面是使用Java递归实现判断二叉树是否镜像的示例代码 class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val val;} }public class MirrorBinaryTree {public static boolean isMirror(TreeNode root) {if (root null) {return true;}return isSymmetric(root.left, root.right);}private static boolean isSymmetric(TreeNode left, TreeNode right) {if (left null right null) {return true;}if (left null || right null || left.val ! right.val) {return false;}return isSymmetric(left.left, right.right) isSymmetric(left.right, right.left);}public static void main(String[] args) {TreeNode root new TreeNode(1);root.left new TreeNode(2);root.right new TreeNode(2);root.left.left new TreeNode(3);root.left.right new TreeNode(4);root.right.left new TreeNode(4);root.right.right new TreeNode(3);boolean result isMirror(root);System.out.println(二叉树是否镜像: result);} }         在示例代码中isMirror方法接受一个二叉树的根节点作为输入使用递归判断二叉树是否镜像。在isSymmetric方法中首先判断左右子树是否都为空如果是则表示当前节点及其子树镜像对称。其次判断左右子树是否都不为空并且当前节点的值相同如果是则递归判断左子树的左子树和右子树的右子树以及左子树的右子树和右子树的左子树是否镜像对称。最后如果上述条件都不满足则表示二叉树不是镜像。         在示例代码的main方法中我们构建了一个镜像二叉树进行测试并打印出结果。         如果二叉树为空也可视为空镜像因此在isMirror方法中增加了对空树的处理。         该算法的时间复杂度为O(N)其中N是二叉树中的节点数量。因为算法需要遍历每个节点且只访问一次所以时间复杂度与节点数量成线性关系。 三面开放式问题40分钟 十三、如何理解前后端分离 前后端分离是一种Web应用开发模式它将用户界面UI及前端业务逻辑与后端服务及数据处理逻辑分开使得前端和后端可独立开发与部署。这种模式带来了一系列的优势同时也对开发流程和架构提出了新的要求。         以下是前后端分离的几个主要方面和好处  1. 角色和技术栈分离 前端Front-end负责展示用户界面和用户交互通常使用HTML、CSS和JavaScript等技术构建使用现代框架和库如React、Vue、Angular进行丰富的交互式体验的开发。后端Back-end负责处理业务逻辑、数据库交云、认证授权等服务器端功能可以使用各种编程语言和框架如Java Spring Boot、Python Django、Node.js Express等。  2. 开发和部署独立 开发人员可以专注于各自擅长的领域前端开发者专注于用户体验和界面构建后端开发者专注于数据处理和业务规则实现。前后端代码可以分别部署在不同的服务器上提供更灵活的扩展和管理选项。  3. 通讯通过API 前后端之间通过定义良好的API接口交互通常是RESTful API或GraphQL等形式。使用JSON或XML等数据交换格式前端通过HTTP请求与后端通讯。  4. 增强用户体验 由于前端可独立更新所以能够快速响应市场变化提高用户体验。前端可实现单页面应用SPA动态加载内容而无需重新加载整个页面。  5. 提高开发效率 前后端分离允许前后端团队并行工作只要API契约定义好双方即可独立进行开发。可利用各种前端开发和调试工具加快前端开发进度。  6. 便于扩展和维护 各自的更新和维护不会互相影响减少了开发和部署的复杂性。由于前后端的解耦更容易对系统进行扩展和整合新技术。         尽管前后端分离带来了上述优点但同时也带来了一些挑战例如跨域资源共享CORS问题、API版本管理、前后端接口联调需要更好的沟通和文档支持等。解决这些挑战需要团队之间良好的沟通以及合适的工具和流程来协调工作。 十四、有哪些后端开发经验做了什么 应该准备一个简洁而详细的回答来展示你的后端开发技能、所使用的技术栈以及你在以往项目中的具体贡献。以下是几个方面参考  1. 概述后端技术栈         开始时简要介绍你使用过的后端语言和框架如Java/Spring Boot、C#/ASP.NET、Python/Django、Node.js/Express等。  2. 描述特定的项目经验         提供一个或几个具体的项目例子这些例子应该能够反映出你在后端开发方面的专业水平和经验。对每个项目介绍以下信息 项目的目标和你的角色。使用的技术栈和工具。你主要负责的功能和任务。所解决的关键问题和挑战。项目的成果和你对成功所做的贡献。  3. 展示解决问题的能力         谈谈你在后端开发中遇到的最有挑战性的问题以及你是如何解决这些问题的。  4. 强调团队合作         如果适用讨论你如何与前端开发者、设计师和其他团队成员合作以确保项目的顺利进行。  5. 讨论性能和安全         如果你有在项目中针对性能优化或安全措施做出贡献的经验一定要提到。  6. 提供维护和扩展的经验         如果你参与了现有项目的维护或是为系统提供了扩展功能说明你的工作如何提高了代码质量、系统可靠性或用户体验。  7. 量化成果         如果可能提供一些量化的结果比如性能提升的百分比、处理的请求量、减少的加载时间等。           示例         “我在后端开发方面有5年的经验主要使用Java和Spring Boot框架。我参与过多个企业级项目例如开发了一个支持数百万并发用户的电子商务平台其中我负责实现订单处理系统和用户身份验证模块。         除了Java我也使用过Node.js开发API服务并且熟悉数据库技术比如MySQL和MongoDB。在最近的一个项目中我优化了数据库查询使关键操作的速度提高了30%以上。         我还处理过对系统安全的改进。我曾经实现了一套基于OAuth 2.0的权限管理系统确保了我们的用户数据的安全性。团队合作方面我通常与前端开发人员密切协作确定API规格并通过持续集成和代码评审来维护代码质量。我们的协作导致了项目按时交付客户反馈表明用户满意度显著提高。”         根据自己的经验调整这个回答保持真实性并根据应聘的岗位特点强调最相关的部分。 十五、介绍HashMap与TreeMap区别 HashMap 和 TreeMap 是 Java 的两种常用的 Map 实现它们都提供了键-值对存储机制但在内部工作原理和特性上有所不同。以下是它们之间的主要区别  1. 内部结构 HashMap 基于散列表哈希表实现使用哈希函数来确定每个键值对节点的存储位置。TreeMap 基于红黑树实现红黑树是一种自平衡的排序二叉树。  2. 排序 HashMap 不保证任何排序键值对的存储是根据哈希值来决定的迭代它时得到的顺序是无序的。TreeMap 根据键的自然顺序或者构造时所指定的 Comparator 进行排序。这意味着键会按升序排列或者按 Comparator 实现的顺序排列。  3. 时间复杂度 HashMap 提供了常数时间的性能即 O(1)对于 get、put 和 remove 操作理想情况下是这样。但是在最坏的情况下例如当所有元素都映射到同一个桶中时性能可能会退化到 O(n)。TreeMap 保证了 get、put 和 remove 操作的时间复杂度为 O(log n)因为它是基于树的。  4. null 值 HashMap 允许键和值为 null意味着你可以将 null 作为一个键或值插入到 HashMap 中。TreeMap 不允许键为 null因为它需要按照某种顺序对键进行排序但允许值为 null。  5. 线程安全 两者都不是线程安全的。在多线程环境下如果没有正确的同步任何结构性修改都可能引发并发问题。如果需要线程安全可以通过 Collections.synchronizedMap 来包装 HashMap或者使用 ConcurrentHashMap 代替 HashMap。而 TreeMap 没有直接的线程安全对应但可以考虑使用 ConcurrentSkipListMap。  6. 性能 HashMap 通常在大部分场景下提供更好的性能尤其是在添加和查询元素时。而 TreeMap 在维持映射的有序状态方面表现更好尤其适用于需要有序遍历键时。         选择 HashMap 还是 TreeMap 取决于应用程序的具体需求。如果不需要排序并且想要最快的访问速度HashMap 是较好的选项。如果需要一个总是处于排序状态的键集合TreeMap 是更加适合的选择。 十六、⽤HashMap实现⼀个有过期功能的缓存 要使用 HashMap 实现一个具有过期功能的缓存可以创建一个包装类这个类将包含每个缓存项的值和过期时间。我们可以在每次访问缓存时检查该项是否过期。如果过期我们就从缓存中移除这个项。另外我们还可以创建一个维护过期项的线程或定时任务来定期清理过期的缓存项。 下面是一个简单的缓存实现使用了 HashMap import java.util.concurrent.*;public class ExpiringCacheK, V {// 缓存项类包含值和过期时间private class CacheItem {V value;long expiryTime;public CacheItem(V value, long expiryTime) {this.value value;this.expiryTime expiryTime;}}private final ConcurrentHashMapK, CacheItem cacheMap;private final ScheduledExecutorService executorService;public ExpiringCache() {cacheMap new ConcurrentHashMap();executorService Executors.newSingleThreadScheduledExecutor();// 定期执行过期缓存清理任务executorService.scheduleAtFixedRate(() - {long currentTime System.currentTimeMillis();cacheMap.entrySet().removeIf(entry - currentTime entry.getValue().expiryTime);}, 1, 1, TimeUnit.SECONDS);}// 向缓存添加项并设置过期时间public void put(K key, V value, long expiryDurationInMillis) {long expiryTime System.currentTimeMillis() expiryDurationInMillis;cacheMap.put(key, new CacheItem(value, expiryTime));}// 从缓存中获取项如果不存在或已过期则返回 nullpublic V get(K key) {CacheItem item cacheMap.get(key);if (item ! null System.currentTimeMillis() item.expiryTime) {return item.value;}cacheMap.remove(key); // 如果已过期移除return null;}public void shutdown() {executorService.shutdownNow();} }         在以上代码中我们创建了一个内部类 CacheItem 来存储缓存的值和该项的过期时间。通过 ScheduledExecutorService, 我们安排了定期执行的任务以清理所有过期的缓存项。put 方法添加缓存项时需要指定一个过期时间。get 方法只返回未过期的缓存项如果检测到缓存项已过期则将其移除。         这个实现是线程安全的因为我们使用了 ConcurrentHashMap它是一个支持完全并发的哈希表。我们也使用了 ScheduledExecutorService 来定期检查和清除过期项。         请记得在停止使用缓存或者程序结束时调用 shutdown() 方法来关闭 ScheduledExecutorService。这很重要因为我们不希望后台线程在不需要时继续运行。                  Google 的 Guava 库中包含了一个功能强大的缓存实现 Cache 类你可以自己用Cache来实现缓存过期功能试试。         下面是使用 Guava 缓存的一个简单例子 import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache;import java.util.concurrent.TimeUnit;public class GuavaCacheExample {public static void main(String[] args) throws Exception {// 创建缓存LoadingCacheString, String cache CacheBuilder.newBuilder().maximumSize(100) // 最多缓存项.expireAfterWrite(10, TimeUnit.MINUTES) // 写入10分钟后过期.build(new CacheLoaderString, String() {public String load(String key) {return fetchData(key);}});// 从缓存中取值可能直接取缓存或者加载数据后缓存String value cache.get(myKey);System.out.println(Value for myKey: value);// 直接向缓存里添加一个键值对cache.put(anotherKey, anotherValue);// 得到某个键对应的值如果存在System.out.println(Value for anotherKey: cache.getIfPresent(anotherKey));}// 示范的数据加载方法实际应用中应更复杂例如数据库查询操作private static String fetchData(String key) {// Here you should implement actual data fetching logicreturn Data for key;} }         在这个例子中我们使用 Guava 来创建一个缓存它有一个最大项数限制并且每个缓存项在写入10分钟后会自动过期。CacheLoader 被用来定义如何加载数据到缓存中。当你调用 get() 方法时Guava 自动使用定义好的加载机制来提供值。如果缓存中已经有这个值了就会立刻返回。         Guava 的 Cache 类也提供了很多其他功能比如监听器手动移除缓存统计等。         如果需要在商业项目中使用缓存推荐使用成熟的缓存框架比如 GuavaCaffeineEhcache 或者 Hazelcast 等。这些框架提供了更复杂的缓存策略性能监控以及和其他技术的集成。 十七、平时怎么学习新知识 下面是一些常见的学习方法和技巧 设定学习目标开始学习前先设定清晰的、具体的学习目标这可以帮助你保持专注并衡量自己的进步。 按计划学习建立一个学习计划为每个学习阶段设定时间表和里程碑按计划执行可以提高学习效率。 多样化学习渠道使用书籍、在线课程、视频教学、博客文章、技术文档等不同的学习资源可以帮助你全面理解新知识。 实践操作通过实际编程、设计、写作或其他实践活动将所学知识运用到实际中去实践是检验学习成果的最佳方式。 参与讨论加入学习小组或在线社区参与讨论可以让你从不同的角度理解新知识同时解答他人问题也是一种很好的学习方式。 建立联系尝试将新学的知识和你已经知道的知识联系起来这样有助于记忆和深化理解。 定期复习周期性地检查和复习你所学的内容避免遗忘并确保知识点能够牢固记在脑中。 灵活调整在学习过程中根据自己的学习进展和理解情况适当调整学习目标和计划。 保持好奇心和耐心学习新知识可能会遇到困难和挑战保持开放和有好奇心的态度是很重要的耐心地对待每一步学习过程。 运用现代技术辅助学习可以利用各种软件和Apps来辅助学习比如使用笔记软件整理学习笔记用时间管理工具追踪学习时间等。 十八、最近看了什么书 这个就⾃由发挥了。         要回答好这个问题一定要保持看书学习的状态否则肯定回答不好这个问题至少不能瞎编多问两个问题就被看穿。         所以随时保持学习充电状态。一起加油未来可期。
http://www.tj-hxxt.cn/news/227035.html

相关文章:

  • 网站上的链接怎么做的怎么查一个网站是什么程序做的
  • 外贸网站运营是做什么的wordpress模板yunnut
  • 浙江省建设监理协会管网站公式wordpress
  • 怎样做才能让自己的网站深圳便宜的网站建设
  • 视觉差网站制作vps网站空间
  • 找一个网站做优化分析视频设计师是干什么的
  • 怎么看一个网站是什么程序做的wordpress 什么值得买
  • 网站建设和销售有关吗做网站fjfzwl
  • 蚌埠网站建设公司网站建设的目标的意思
  • 同时优化几个网站wordpress ios 源码
  • 山西建筑劳务网站长沙网站设计流程
  • 大型网站开发 书籍天津红桥网站建设
  • 域名解析映射到网站空间怎么做中国文明网联盟网站建设
  • 做网站会员金字塔系统wordpress rss 订阅
  • 网站卖东西怎么做c2c平台的特点是什么
  • 云南微网站制作哪家好国内有实力的软件开发公司
  • 券多多是谁做的网站个人nas做网站
  • 做门票售卖网站广州网站建设公司乐云seo598
  • h5模板网站模板企业网站推广的方法有哪几种
  • 网站建设与管理课程总结免费拓客软件排行榜
  • 网站建设和软件开发哪个有前途手机商城网站制作
  • 微信做单页的网站建外文网站
  • flutter 如何做网站网站空白页黑链
  • 如何建设音乐网站天津做网站找哪家公司
  • 运城 网站制作高端网站开发哪家专业
  • 免费网站建设找云狄上海著名网站建设
  • 月嫂网站源码免费建设论坛网站
  • 网站地图html怎么做传播易广告投放平台
  • h5网站不利于优化吗江苏纬信网站建设
  • 代码网站怎么做的学网络工程师培训学校