【廖雪峰官方网站/Java教程】反射

news/2024/5/20 4:45:18/文章来源:https://blog.csdn.net/Allenlzcoder/article/details/105159285

注:本文参考自:https://www.liaoxuefeng.com/wiki/1252599548343744/1255945147512512
什么是反射?
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
正常情况下,如果我们要调用一个对象的方法,或者访问一个对象的字段,通常会传入对象实例。反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。

1.Class类

1.1.Class类和反射(reflection)基本概念

除了int等基本类型外,Java的其他类型全部都是class(包括interface)。例如:

String
Object
Runnable
Exception

而class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。注意:这里的Class类型是一个名叫Class的class。它长这样:

public final class Class {private Class() {}
}

以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:

Class cls = new Class(String);

这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。
所以,JVM持有的每个Class实例都指向一个数据类型(class或interface)。
在这里插入图片描述
一个Class实例包含了该class的所有完整信息:
在这里插入图片描述
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
这种通过Class实例获取class信息的方法称为反射(Reflection)。

1.2.三种方法获取一个class的Class实例

1.2.1.方法1:直接通过一个class的静态变量class获取:

Class cls = String.class;

1.2.2.方法2:实例变量的getClass()方法

如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

String s = "Hello";
Class cls = s.getClass();

1.2.3.方法3:静态方法Class.forName()获取

如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:

Class cls = Class.forName("java.lang.String");

1.3.Class实例比较

1.3.1.利用"=="比较两个Class实例

因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例:

Class cls1 = String.class;String s = "Hello";
Class cls2 = s.getClass();boolean sameClass = cls1 == cls2; // true

1.3.2.与instanceof的区别

两者区别如下:

Integer n = new Integer(123);boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class

用instanceof不但匹配指定类型,还匹配指定类型的子类。而用"==“判断class实例可以精确地判断数据类型,但不能作子类型比较。
通常情况下,我们应该用instanceof判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型。只有在需要精确判断一个类型是不是某个class的时候,我们才使用”=="判断class实例。

1.4.数组也是一种Class

注意到数组(例如String[])也是一种Class,而且不同于String.class,它的类名是[Ljava.lang.String。此外,JVM为每一种基本类型如int也创建了Class,通过int.class访问。
如果获取到了一个Class实例,我们就可以通过该Class实例来创建对应类型的实例:

// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance(); // 相当于 new String()

上述代码相当于new String()。通过Class.newInstance()可以创建类实例,它的局限是:只能调用【public && 无参数】构造方法。带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用。

1.5.动态加载

JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。

2.访问字段

2.1.Class类中访问字段

对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。
我们先看看如何通过Class实例获取字段信息。Class类提供了以下几个方法来获取字段:

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

一个Field对象包含了一个字段的所有信息:

  • getName():返回字段名称,类型是String,例如,“name”;
  • getType():返回字段类型,也是一个Class实例,例如,String.class;
  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
    以String类的value字段为例,它的定义是:
public final class String {private final byte[] value;
}

我们用反射获取该字段的信息,代码如下:

Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false

2.2.获取字段值

利用反射拿到字段的一个Field实例只是第一步,我们还可以拿到一个实例对应的该字段的值。
例如,对于一个Person实例,我们可以先拿到name字段对应的Field,再获取这个实例的name字段的值:

// reflection
import java.lang.reflect.Field;public class Main {public static void main(String[] args) throws Exception {Object p = new Person("Xiao Ming");Class c = p.getClass();Field f = c.getDeclaredField("name");Object value = f.get(p);System.out.println(value); // "Xiao Ming"}
}class Person {private String name;public Person(String name) {this.name = name;}
}

上述代码先获取Class实例,再获取Field实例,然后,用Field.get(Object)获取指定实例的指定字段的值。
运行代码,如果不出意外,会得到一个IllegalAccessException,这是因为name被定义为一个private字段,正常情况下,Main类无法访问Person类的private字段。要修复错误,可以将private改为public,或者,在调用Object value = f.get§;前,先写一句:

f.setAccessible(true);

调用Field.setAccessible(true)的意思是,别管这个字段是不是public,一律允许访问。
可以试着加上上述语句,再运行代码,就可以打印出private字段的值。

2.3.设置字段值

通过Field实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。
设置字段值是通过Field.set(Object, Object)实现的,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值。示例代码如下:

// reflection
import java.lang.reflect.Field;public class Main {public static void main(String[] args) throws Exception {Person p = new Person("Xiao Ming");System.out.println(p.getName()); // "Xiao Ming"Class c = p.getClass();Field f = c.getDeclaredField("name");f.setAccessible(true);f.set(p, "Xiao Hong");System.out.println(p.getName()); // "Xiao Hong"}
}class Person {private String name;public Person(String name) {this.name = name;}public String getName() {return this.name;}
}

运行上述代码,打印的name字段从Xiao Ming变成了Xiao Hong,说明通过反射可以直接修改字段的值。
同样的,修改非public字段,需要首先调用setAccessible(true)。

3.调用方法

3.1.Class类中获取Method

我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有Method信息。Class类提供了以下几个方法来获取Method:

  • Method getMethod(name, Class…):获取某个public的Method(包括父类) Method
  • getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类) Method[]
  • getMethods():获取所有public的Method(包括父类) Method[]
  • getDeclaredMethods():获取当前类的所有Method(不包括父类)
public class Main {public static void main(String[] args) throws Exception {Class stdClass = Student.class;// 获取public方法getScore,参数为String:System.out.println(stdClass.getMethod("getScore", String.class));// 获取继承的public方法getName,无参数:System.out.println(stdClass.getMethod("getName"));// 获取private方法getGrade,参数为int:System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));}
}class Student extends Person {public int getScore(String type) {return 99;}private int getGrade(int year) {return 1;}
}class Person {public String getName() {return "Person";}
}

上述代码首先获取Student的Class实例,然后,分别获取public方法、继承的public方法以及private方法,打印出的Method类似:

public int Student.getScore(java.lang.String)
public java.lang.String Person.getName()
private int Student.getGrade(int)

一个Method对象包含一个方法的所有信息:

  • getName():返回方法名称,例如:“getScore”;
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

3.2.调用方法(通过反射执行Method)

当我们获取到一个Method对象时,就可以对它进行调用。我们以下面的代码为例:

String s = "Hello world";
String r = s.substring(6); // "world"

如果用反射来调用substring方法,需要以下代码:

public class Main {public static void main(String[] args) throws Exception {// String对象:String s = "Hello world";// 获取String substring(int)方法,参数为int:Method m = String.class.getMethod("substring", int.class);// 在s对象上调用该方法并获取结果:String r = (String) m.invoke(s, 6);// 打印调用结果:System.out.println(r);}
}

注意到substring()有两个重载方法,我们获取的是String substring(int)这个方法。思考一下如何获取String substring(int, int)方法。

Method m = String.class.getMethod("substring", int.class, int.class);
// 在s对象上调用该方法并获取结果:
String r = (String) m.invoke(s, 4, 6);

对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。

3.3.调用静态方法

如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。我们以Integer.parseInt(String)为例:

public class Main {public static void main(String[] args) throws Exception {// 获取Integer.parseInt(String)方法,参数为String:Method m = Integer.class.getMethod("parseInt", String.class);// 调用该静态方法并获取结果:Integer n = (Integer) m.invoke(null, "12345");// 打印调用结果:System.out.println(n);}
}

3.4.调用非public方法

和Field类似,对于非public方法,我们虽然可以通过Class.getDeclaredMethod()获取该方法实例,但直接对其调用将得到一个IllegalAccessException。为了调用非public方法,我们通过Method.setAccessible(true)允许其调用:

// reflection
import java.lang.reflect.Method;public class Main {public static void main(String[] args) throws Exception {Person p = new Person();Method m = p.getClass().getDeclaredMethod("setName", String.class);m.setAccessible(true);m.invoke(p, "Bob");System.out.println(p.name);}
}class Person {String name;private void setName(String name) {this.name = name;}
}

此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全。

3.5.多态

我们来考察这样一种情况:一个Person类定义了hello()方法,并且它的子类Student也覆写了hello()方法,那么,从Person.class获取的Method,作用于Student实例时,调用的方法到底是哪个?

// reflection
import java.lang.reflect.Method;public class Main {public static void main(String[] args) throws Exception {// 获取Person的hello方法:Method h = Person.class.getMethod("hello");// 对Student实例调用hello方法:h.invoke(new Student());}
}class Person {public void hello() {System.out.println("Person:hello");}
}class Student extends Person {public void hello() {System.out.println("Student:hello");}
}

运行上述代码,发现打印出的是Student:hello,因此,使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。上述的反射代码:

Method m = Person.class.getMethod("hello");
m.invoke(new Student());

实际上相当于:

Person p = new Student();
p.hello();

4.调用构造方法

此部分可结合《Java从入门到精通》第16章的内容一起看。
我们通常使用new操作符创建新的实例:

Person p = new Person();

如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法:

Person p = Person.class.newInstance();

调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:

import java.lang.reflect.Constructor;public class Main {public static void main(String[] args) throws Exception {// 获取构造方法Integer(int):Constructor cons1 = Integer.class.getConstructor(int.class);// 调用构造方法:Integer n1 = (Integer) cons1.newInstance(123);System.out.println(n1);// 获取构造方法Integer(String)Constructor cons2 = Integer.class.getConstructor(String.class);Integer n2 = (Integer) cons2.newInstance("456");System.out.println(n2);}
}

通过Class实例获取Constructor的方法如下:

  • getConstructor(Class…):获取某个public的Constructor;
  • getDeclaredConstructor(Class…):获取某个Constructor;
  • getConstructors():获取所有public的Constructor;
  • getDeclaredConstructors():获取所有Constructor。

注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。

5.获取继承关系

在1.2小节中总结了获取一个类Class实例的3种方法。

5.1.获取父类的Class

有了Class实例,我们还可以获取它的父类的Class:

public class Main {public static void main(String[] args) throws Exception {Class i = Integer.class;Class n = i.getSuperclass();System.out.println(n);Class o = n.getSuperclass();System.out.println(o);System.out.println(o.getSuperclass());}
}

运行上述代码,可以看到,Integer的父类类型是Number,Number的父类是Object,Object的父类是null。除Object外,其他任何非interface的Class都必定存在一个父类类型。

5.2.获取interface

由于一个类可能实现一个或多个接口,通过Class我们就可以查询到实现的接口类型。例如,查询Integer实现的接口:

// reflection
import java.lang.reflect.Method;public class Main {public static void main(String[] args) throws Exception {Class s = Integer.class;Class[] is = s.getInterfaces();for (Class i : is) {System.out.println(i);}}
}

要特别注意:getInterfaces()只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型。
此外,对所有interface的Class调用getSuperclass()返回的是null,获取接口的父接口要用getInterfaces():

System.out.println(java.io.DataInputStream.class.getSuperclass()); // java.io.FilterInputStream,因为DataInputStream继承自FilterInputStream
System.out.println(java.io.Closeable.class.getSuperclass()); // null,对接口调用getSuperclass()总是返回null,获取接口的父接口要用getInterfaces()

如果一个类没有实现任何interface,那么getInterfaces()返回空数组。

5.3.继承关系

当我们判断一个实例是否是某个类型时,正常情况下,使用instanceof操作符:

Object n = Integer.valueOf(123);
boolean isDouble = n instanceof Double; // false
boolean isInteger = n instanceof Integer; // true
boolean isNumber = n instanceof Number; // true
boolean isSerializable = n instanceof java.io.Serializable; // true

如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():

// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer

6.动态代理

6.1.静态代码写法

有没有可能不编写实现类,直接在运行期创建某个interface的实例呢?
这是可能的,因为Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例。
什么叫运行期动态创建?听起来好像很复杂。所谓动态代理,是和静态相对应的。我们来看静态代码怎么写:
定义接口:

public interface Hello {void morning(String name);
}

编写实现类:

public class HelloWorld implements Hello {public void morning(String name) {System.out.println("Good morning, " + name);}
}

创建实例,转型为接口并调用:

Hello hello = new HelloWorld();
hello.morning("Bob");

6.2.动态代理【未细看】

还有一种方式是动态代码,我们仍然先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
一个最简单的动态代理实现如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(method);if (method.getName().equals("morning")) {System.out.println("Good morning, " + args[0]);}return null;}};Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), // 传入ClassLoadernew Class[] { Hello.class }, // 传入要实现的接口handler); // 传入处理调用方法的InvocationHandlerhello.morning("Bob");}
}interface Hello {void morning(String name);
}

在这里插入图片描述
动态代理实际上是JDK在运行期动态创建class字节码并加载的过程,它并没有什么黑魔法,把上面的动态代理改写为静态实现类大概长这样:

public class HelloDynamicProxy implements Hello {InvocationHandler handler;public HelloDynamicProxy(InvocationHandler handler) {this.handler = handler;}public void morning(String name) {handler.invoke(this,Hello.class.getMethod("morning"),new Object[] { name });}
}

其实就是JDK帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码),并不存在可以直接实例化接口的黑魔法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_712922.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【廖雪峰官方网站/Java教程】泛型

泛型是一种“代码模板”&#xff0c;可以用一套代码套用各种类型。 1.什么是泛型 1.1.泛型入门概念介绍 为了在ArrayList中兼容不同类型等元素&#xff0c;我们必须把ArrayList变成一种模板&#xff1a;ArrayList<T>&#xff0c;代码如下&#xff1a; public class …

【廖雪峰官方网站/Java教程】多线程(1)

多线程是Java最基本的一种并发模型&#xff0c;本章我们将详细介绍Java多线程编程。 1.多线程基础 1.1.进程 在计算机中&#xff0c;我们把一个任务称为一个进程&#xff0c;浏览器就是一个进程&#xff0c;视频播放器是另一个进程&#xff0c;类似的&#xff0c;音乐播放器…

【廖雪峰官方网站/Java教程】多线程(2)

1.使用wait和notify 1.1.多线程协调 在Java程序中&#xff0c;synchronized解决了多线程竞争的问题。例如&#xff0c;对于一个任务管理器&#xff0c;多个线程同时往队列中添加任务&#xff0c;可以用synchronized加锁&#xff1a; class TaskQueue {Queue<String> q…

【廖雪峰官方网站/Java教程】多线程(3)

1.使用线程池 1.1.ExecutorService介绍 Java语言虽然内置了多线程支持&#xff0c;启动一个新线程非常方便&#xff0c;但是&#xff0c;创建线程需要操作系统资源&#xff08;线程资源&#xff0c;栈空间等&#xff09;&#xff0c;频繁创建和销毁大量线程需要消耗大量时间。…

【廖雪峰官方网站/Java教程】Maven基础

Maven是一个Java项目管理和构建工具&#xff0c;它可以定义项目结构、项目依赖&#xff0c;并使用统一的方式进行自动化构建&#xff0c;是Java项目不可缺少的工具。 1.Maven介绍 1.1.Maven功能及项目结构 1.1.1.Maven主要功能 Maven就是是专门为Java项目打造的管理和构建工…

【廖雪峰官方网站/Java教程】设计模式(一)

0.概述.设计模式的基本概念及原则 设计模式&#xff0c;即Design Patterns&#xff0c;是指在软件设计中&#xff0c;被反复使用的一种代码设计经验。使用设计模式的目的是为了可重用代码&#xff0c;提高代码的可扩展性和可维护性。 为什么要使用设计模式&#xff1f;根本原因…

[转]上海新东方vs新东方,SEO实战

引用前言&#xff1a;半夜无聊上网&#xff0c;看到了这篇文章&#xff0c;觉得还不错&#xff0c;看了收获不小&#xff0c;所以就转过来了。来源 非常郑重地声明一下&#xff08;文章发表约20小时后补充&#xff09; 我不得不承认&#xff0c;这篇文章有点“软”&#xff0c;…

前端web:响应式网站开发的现状你了解吗?

当企业对网络营销有了更深的认识时&#xff0c;不管是大企业还是小企业&#xff0c;都已经建立了自己的个性化响应网站&#xff0c;都希望利用互联网这一新形态的市场&#xff0c;以及网络营销的新型营销模式。如今建立响应式网站也是一种趋势&#xff0c;利用互联网的优势&…

学用MVC4做网站五:5.4删除文章

前几天把添加、修改功能都做了&#xff0c;今天开始写删除功能。删除文章既要删除文章本身同时也要在公共模型中删除对应项。 首先写从数据库中删除文章的函数。打开ArticleRepository修改Delete的函数。有上次的教训这次明白了传递的id应该是公共模型id。 /// <summary>…

三分钟免费搞定网站在线客服,利用PowerTalkBox控件制作而成,为大家提供比较好的示例...

下载地址:http://download.csdn.net/source/1876659 必须安装.net2.0才可以支持网站服务端 内带完整的安装流程,支持飞信功能,使您不在电脑前时也可以用手机交流. 可以利用以下的js代码实现浮动窗口的功能. <script languagejavascript>var cao_x,cao_y; function cao888…

amazon s3_在Amazon S3上托管静态网站

amazon s3Static website hosting on Amazon S3 is one of the very popular use cases of Amazon S3. It allows you to host an entire static website and on very low cost. Amazon S3 is a highly available and scalable hosting solution.Amazon S3上的静态网站托管是Am…

一个可以实时查相关电子产品价格的网站

香港价格网&#xff0c;里面的价格和香港的百老汇、丰泽等的价格几乎同步&#xff0c;相差不大&#xff0c;有很大的参考价值&#xff0c;对于准备去香港买电子产品的网友来说&#xff0c;是个非常好的网站&#xff0c;特别分享&#xff1a; http://www.price.com.hk/转载于:ht…

简单高效!25个漂亮的简约风格网站设计作品

在过去几年里&#xff0c;网站设计领域发生了巨大变化。除了 RWD&#xff08;响应式网页设计&#xff09;和 Web 字体的革命&#xff0c;现代设计的发展趋势迅速流行扁平化的配色方案&#xff0c;网页排版变得更加重要&#xff0c;重点已放在内容第一。最后&#xff0c;页面加载…

增城seo搜索引擎优化_搜索引擎seo优化主要从哪里入手?

首先我们应该了解什么是搜索引擎优化以及网站搜索引擎seo优化的价值&#xff0c;从基础开始逐步深入&#xff0c;下面拓王朝所要讲的都是一些理论知识&#xff0c;很好理解&#xff0c;有不同见解欢迎评论。SEO优化SEO搜索引擎优化&#xff0c;是指通过采用易于搜索引擎索引和排…

[转]使用ThinkPHP框架快速开发网站(多图)

本文转自&#xff1a;http://blog.csdn.net/ruby97/article/details/7574851 这一周一直忙于做实验室的网站&#xff0c;基本功能算是完成了。比较有收获的是大概了解了ThinkPHP框架。写一些东西留作纪念吧。如果对于同样是Web方面新手的你有一丝丝帮助&#xff0c;那就更好了挖…

《大型网站技术架构》读书笔记[3] - 架构核心五要素

架构设计中要考虑的核心五要素&#xff1b; 性能、可用性、扩展性、伸缩性、安全性 性能 性能的测试指标 响应时间 应用执行一个操作需要的时间&#xff0c;包括从发出请求开始到收到最后响应数据所需要的时间。响应时间是系统最重要的性能指标&#xff0c;直观地反映了系统的“…

java抓取网页数据_Golang丨Java丨Python爬虫实战—Boss直聘网站数据抓取

我们分别通过Golang、Python、Java三门语言&#xff0c;分别实现对Boss直聘网站的招聘数据进行爬取。首先打开Boss直聘网站&#xff1a;然后我们在职位类型中输入Go或者Golang关键字&#xff1a;然后我们可以看到一个列表&#xff0c;和Go语言相关的各种招聘职位&#xff0c;还…

我的网站被黑了,关键词被劫持,总结一下是怎么解决的。

1、发现被黑&#xff0c;网站被黑的症状 两年前自己用wordpress搭了一个网站&#xff0c;平时没事写写文章玩玩。但是前些日子&#xff0c;突然发现网站的流量突然变小&#xff0c;site了一下百度收录&#xff0c;发现出了大问题&#xff0c;网站被黑了。大多数百度抓取收录的页…

一个大图切成几个小图加载速度更快_谷歌SEO页面速度的重要性

什么是页面速度&#xff1f;页面速度是指网页加载所需的时间。一个页面的加载速度是由几个不同的因素决定的&#xff0c;包括网站的服务器、页面文件大小和图片压缩。也就是说&#xff0c;"页面速度 "并不像 "网页速度 "那么重要。"页面速度 "并…

大学计算机思维导图_3款免费在线思维导图网站,你一定要收藏一个!

1&#xff1a;迅捷画图https://www.liuchengtu.com/迅捷画图是一个专业的思维导图、流程图制作网站。支持在线创作流程图、思维导图、组织结构、ER图、网络拓扑图、UML图等等。接下来说说特色&#xff1a;l 支持导出多种格式&#xff0c;如JPG、PNG、PDF文件、txt文本等格式l 提…