跳至主要內容

Java

大约 6 分钟

Java

Java 环境配置

安装 jdk

Windows

安装完后需要配置环境变量

JAVA_HOME
jdk安装目录
CLASSPATH
.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
Path
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin

配完后

java
javac

看下有正常回显即可



IDEA

ubuntu 安装 IDEA

直接远程连接安装即可

image-20220923185210262


Tomcat

Apache Tomcat® - Apache Tomcat 8 Software Downloads --- Apache Tomcat® - Apache Tomcat 8 软件下载open in new window

image-20230613111231319

Windows 下直接下 Installer 版本即可

image-20230613111315190

安装时会默认 Server Shutdown Port -1, 意味着关闭了监听 shutdown 命令的端口, 后续启停可以在 Windows 服务(services.msc)中进行操作


Java 反射

Java 反射详解 - YSOcean - 博客园 (cnblogs.com)open in new window


Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为(准)动态语言的一个关键性质

为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。

反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;

但是需要注意的是反射使用不当会造成很高的资源消耗!


得到 Class 的三种方式

比如新建一个 Person 类

package reflect;

public class Person {
    private String name = "Jacob";
    public int age = 20;
    public Person(){
        System.out.println("Person()");
    }
    private void say(){
        System.out.println("Hello World!");
    }
    public void work(){
        System.out.println("I'm working!");
    }
}

image-20221207152138868

现在要在其他类中获取一个 Person 对象的 class 可以使用如下三种方式:

package reflect;

public class reflect {
    // 1. 通过对象调用 getClass() 方法获取 Person 的 Class;
    // 通常用于传入一个 Object 对象, 但是不知道具体是什么类, 通过 getClass() 方法获取 Class 对象;
    public void by_getClass() {
        System.out.println("1. 通过对象调用 getClass() 方法获取 Person 的 Class;");
        Person person1 = new Person();
        Class c1 = person1.getClass();
        System.out.println(c1.getName());
    }

    // 2.直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
    // 这说明每个类都有一个隐含的静态成员变量 class
    public void by_class() {
        System.out.println("2.直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高");
        Class c2 = Person.class;
        System.out.println(c2.getName());
    }

    // 3.通过 Class 类的静态方法 forName(String className) 得到
    // 该方法将类的全名(包括包名) 作为参数,返回对应的 Class 对象
    // 用的最多, 但可能抛出 ClassNotFoundException 异常
    public void by_forName() throws ClassNotFoundException {
        System.out.println("3.通过 Class 类的静态方法 forName(String className) 得到");
        Class c3 = Class.forName("reflect.Person");
        System.out.println(c3.getName());
    }

}

import reflect.reflect;

public class test {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        reflect r = new reflect();
        r.by_getClass();
        r.by_class();
        try {
            r.by_forName();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

image-20221207155641124


命令执行

正常写法

java.lang.Runtime.getRuntime().exec("calc");

反射写法:

try {
    Class<?> cls = Class.forName("java.lang.Runtime");
    Method method = cls.getMethod("getRuntime");
    Runtime runtime = (Runtime) method.invoke(null);
    runtime.exec("calc");
} catch (Exception e) {
    e.printStackTrace();
}
  • line3Method 指的是 java.lang.reflect.Method 类, 在 Java 中,java.lang.reflect.Method 类提供了关于类或接口上单个方法的信息和访问权限。可以使用 java.lang.reflect.Method 类的实例来获取方法的信息(如返回类型、参数类型、访问修饰符等) 或者对它进行调用。
  • line3getMethod 方法被用来获取名为 getRuntime 的方法(这是 java.lang.Runtime 类的一个静态方法)。然后,invoke 方法被用来调用这个获取到的方法。
  • 因为 getRuntime 是一个无参数的方法,所以 invoke 方法被调用时只传入了一个 null 参数,这个 null 参数表示当前正在调用的是一个不需要实例对象的方法(即静态方法)。

将反射写法写为一行:

((Runtime) Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null)).exec("calc");

需要注意的是 Class.getMethod 的返回类型是 java.lang.reflect.Method,而 Method.invoke() 的返回类型是 java.lang.Object

因此,当你试图在返回的 Object 类型上调用 exec 方法时,编译器无法找到 exec 方法,因为 java.lang.Object 类没有定义 exec 方法。

所以这里用的 (Runtime) 来将 invoke 的返回值强制类型转换为 Runtime 类型,因为 execRuntime 类的方法


不加强制类型转换的话可以这样写:

Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(
        Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),
        "calc"
);

首先获取 exec 方法的 Method 对象,然后再调用 invoke 方法,其第一个参数传递了 exec 方法的调用者(Runtime 对象) ,第二个参数传递了 exec 方法的参数(calc) 。

或者通过 String对象.getClass() 来获取 Class 也可以:

"va".getClass().forName("java.lang.Runtime").getMethod("exec", String.class).invoke(
        "va".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),
        "calc"
);

以及这里的字符串是可以拆分再拼接的, 下面这种写法也是可以正确执行的:

Class.forName("java"+".lang.Runtime").getMethod("exec", String.class).invoke(
        Class.forName("java.la"+"ng.Runtime").getMethod("getRuntime").invoke(null),
        "calc"
);
"va".getClass().forName("java.lan"+"g.Runtime").getMethod("exec", String.class).invoke(
        "va".getClass().forName("java.l"+"ang.Runtime").getMethod("getRuntime").invoke(null),
        "calc"
);`