1、什么是 Java 中的基本数据类型?基本数据类型的大小和范围是多少?
Java 中的基本数据类型是语言内置的数据类型,用于存储简单数据,如整数、浮点数、布尔值和字符等。
以下是 Java 中的基本数据类型及其大小和范围:
byte:8 位,范围为 -128 到 127。
short:16 位,范围为 -32,768 到 32,767。
int:32 位,范围为 -2,147,483,648 到 2,147,483,647。
long:64 位,范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
float:32 位,范围为 3.4E-45 到 3.4E+38。
double:64 位,范围为 4.9E-324 到 1.8E+308。
char:16 位,范围为 '\u0000' 到 '\uFFFF'。
boolean:8 位,只有两个值:true 和 false。
2、什么是 Java 中的访问修饰符?Java 中有哪些访问修饰符?
Java 中的访问修饰符用于控制类、变量、方法和构造函数的可访问性,即控制哪些类可以访问它们。Java 中有四种访问修饰符:
public:公有的,可以被任何类访问。
private:私有的,只能被同一个类中的其他成员访问。
protected:受保护的,可以被同一个包中的其他类以及不同包中的子类访问。
无修饰符(默认):默认的,只能被同一个包中的其他类访问。
需要注意的是,private 修饰符可以阻止其他类直接访问该类的成员,但是可以通过该类提供的其他公共方法来间接访问。此外,Java 中的接口中的所有成员默认都是 public 访问修饰符。
3、什么是 Java 中的抽象类和接口?它们之间的区别是什么?
区别如下:
1、
抽象类是一个类,
而接口是一个类型。
2、抽象类可以包含具体方法和抽象方法,
而接口只能包含抽象方法和常量。
3、抽象类前面加上 abstract 关键字,
而接口前面不加任何关键字。
4、抽象类中可以定义变量和具体实现,
而接口中只能定义常量和方法签名。
5、抽象类中的方法可以是具体方法或抽象方法,
而接口中的方法必须是抽象方法。
6、抽象类中的变量可以是具体变量或抽象变量,
而接口中的变量必须是抽象变量。
7、抽象类可以被子类继承或实现,
而接口可以被类实现。
8、抽象类中可以有构造方法,
而接口中不能有构造方法。
4、什么是 Java 中的多态?如何实现多态?
Java 中的多态是一种面向对象编程的概念,它允许不同的对象对相同的消息做出不同的响应,从而提高代码的灵活性和可扩展性。多态性主要通过继承和方法重写实现。
在 Java 中,多态性可以通过以下两种方式实现:
1、
编译时多态性:
通过使用方法重载(Method Overloading)和泛型(Generics)实现。方法重载允许定义多个同名方法,但它们的参数类型、个数或顺序必须不同。编译器会根据调用时传入的参数类型和数量来选择正确的方法。泛型则允许定义可以接受不同类型参数的方法或类,从而实现了编译时多态性。
2、运行时多态性:
通过使用方法重写(Method Overriding)和接口(Interfaces)实现。方法重写允许子类重新定义父类的方法。当调用该方法时,实际执行的是子类中的方法。接口定义了一组方法的签名,但没有具体实现。实现接口的类必须提供这些方法的实现。通过实现接口,可以实现运行时多态性。
总之,Java 中的多态性可以通过继承和方法重写实现编译时多态性,通过接口和实现接口的类实现运行时多态性。
5、什么是 Java 中的重载和重写?它们之间的区别是什么?
Java 中的重载(Overloading)和重写(Overriding)都是面向对象编程的概念,它们的含义和区别如下:
1、重载(Overloading)是指在同一个类中定义多个同名的方法,但它们的参数类型、个数或顺序必须不同。编译器会根据调用时传入的参数类型和数量来选择正确的方法。重载可以提高代码的复用性和可读性,使得方法名能够更清晰地表达方法的功能。
2、重写(Overriding)是指子类重新定义父类的方法。子类中的方法必须与父类中的方法具有相同的名称、参数列表和返回类型。重写的方法可以改变父类方法的实现,从而实现不同的功能。重写是实现运行时多态性的关键。
区别如下:
1、方法名称必须相同:对于重载,方法名称必须相同,但参数类型、个数或顺序必须不同;对于重写,方法名称、参数列表和返回类型必须与父类中的方法相同。
2、访问修饰符不能更严格:重载的方法可以具有不同的访问修饰符,而重写的方法的访问修饰符不能比父类中的方法更严格。
3、返回类型可以相同或更宽松:重载的方法可以具有相同的返回类型或不同的返回类型,但必须满足方法的参数类型和个数;重写的方法的返回类型必须与父类中的方法相同或更宽松(即子类的返回类型必须是父类返回类型的子类)。
4、父类指针指向子类对象时:如果一个父类指针指向一个子类对象,那么调用该指针所指对象的方法时,会自动调用子类中的方法,这就是运行时多态性。通过重载可以实现编译时多态性,但不能实现运行时多态性。
6、什么是 Java 中的异常?Java 中有哪些常见的异常?
Java 中的异常是指在程序运行时发生的错误或异常情况,导致程序无法继续执行。异常处理是 Java 语言中非常重要的部分,可以帮助程序员更好地处理错误和异常情况,提高程序的稳定性和可靠性。
Java 中常见的异常包括:
1、NullPointerException(空指针异常):当程序试图使用空引用变量时,会抛出该异常。
2、ArrayIndexOutOfBoundsException(数组越界异常):当程序试图访问数组中不存在的索引时,会抛出该异常。
3、IllegalArgumentException(非法参数异常):当程序接收到无效的参数时,会抛出该异常。
4、ClassCastException(类转换异常):当程序试图将一个对象强制转换为另一个不兼容的类型时,会抛出该异常。
5、FileNotFoundException(文件未找到异常):当程序试图打开一个不存在的文件时,会抛出该异常。
6、SQLException(SQL 异常):当程序与数据库进行交互时发生错误时,会抛出该异常。
以上只是 Java 中常见的一些异常,实际上还有很多其他的异常类型。在编写程序时,程序员应该认真处理异常情况,以避免程序出现不可预期的行为。
7、什么是 Java 中的反射?如何使用反射?
Java 中的反射是指在程序运行时获取类的信息,并能够动态地创建对象、调用方法、访问和修改属性等。反射是 Java 语言中的一个非常重要的特性,它可以帮助程序员更加灵活地操作类和对象,提高代码的复用性和可扩展性。
使用反射的步骤如下:
1、获取 Class 对象:通过类的 .class 属性或 Class.forName() 方法获取该类的 Class 对象。
2、创建对象:通过 Class 对象的 newInstance() 方法或指定的构造方法创建该类的对象。
3、调用方法:通过 Method 对象的 invoke() 方法调用该类的方法。
4、访问属性:通过 Field 对象的 get() 和 set() 方法访问和修改该类的属性。
下面是一个使用反射的示例代码:
import java.lang.reflect.*;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
// 获取 Class 对象
Class<?> cls = Class.forName("java.util.Date");
// 创建对象
Object obj = cls.newInstance();
// 调用方法
Method method = cls.getMethod("toString");
String result = (String) method.invoke(obj);
System.out.println(result);
// 访问属性
Field field = cls.getField("DAY");
int day = field.getInt(obj);
System.out.println("Today is " + day + ".");
}
}
在上面的示例代码中,我们使用了反射来获取 Date 类的信息,并动态地创建了一个 Date 对象,然后调用了它的 toString() 方法,并访问了它的 DAY 属性。
8、什么是 Java 中的线程?如何创建和启动一个线程?
Java 中的线程是程序中的一个执行单元,可以用来实现并发执行多个任务。线程是 Java 语言中的一个重要概念,可以帮助程序员编写高效的并发程序。
在 Java 中,可以通过继承 Thread 类或实现 Runnable 接口来创建线程。下面分别介绍这两种方式:
继承 Thread 类:
public class MyThread extends Thread {
public void run() {
// 线程执行的代码
}
}
// 创建线程对象并启动线程
MyThread thread = new MyThread();
thread.start();
实现 Runnable 接口:
public class MyRunnable implements Runnable {
public void run() {
// 线程执行的代码
}
}
// 创建线程对象并启动线程
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
在上面的代码中,run() 方法是线程的执行体,其中包含线程要执行的代码。创建线程对象后,通过调用 start() 方法来启动线程。
需要注意的是,Java 中的线程是抢占式调度,即线程的执行时间是由操作系统决定的,而不是由程序员控制的。因此,在编写多线程程序时,需要注意线程安全和同步问题,以避免出现竞态条件和数据不一致等问题。
9、什么是 Java 中的同步和异步?如何实现同步和异步?
Java 中的同步和异步是两种不同的执行方式,用于处理并发编程中的多个任务。
1、同步执行:
同步执行是指一个任务必须等待前一个任务执行完成后才能继续执行,即前一个任务的执行结果对后一个任务的执行有影响。在 Java 中,可以通过顺序执行语句或使用 synchronized 关键字来实现同步。
2、异步执行:
异步执行是指一个任务可以在前一个任务未完成时就开始执行,即前一个任务的执行结果对后一个任务的执行没有影响。在 Java 中,可以通过多线程、Future 和 CompletableFuture 等方式来实现异步。
10、什么是线程和进程?线程和进程的区别是什么?
1、定义不一样,进程是执行中的一段程序,而一个进程中执行中的每个任务即为一个线程。
2、一个线程只可以属于一个进程,但一个进程能包含多个线程。
3、线程无地址空间,它包括在进程的地址空间里。
4、线程的开销或代价比进程的小。
11、什么是线程安全?如何保证线程安全?
线程安全是指多个线程同时访问同一代码段时,保证数据的一致性和正确性。如果一个程序是线程安全的,那么它可以在多个线程同时执行而不会出现错误。
要保证线程安全,可以使用以下方法:
1、使用互斥锁(mutex):互斥锁是一种同步机制,可以确保同一时间只有一个线程访问共享资源。在访问共享资源之前,线程必须获取互斥锁,访问完成后释放锁。
2、使用条件变量(condition variable):条件变量是一种同步机制,用于协调多个线程之间的操作。一个线程等待条件变量满足某个条件时,其他线程可以继续执行。
3、使用原子操作(atomic operations):原子操作是一种特殊的操作,可以确保多个指令的执行是原子的,不会被其他线程中断。原子操作适用于对单个变量的操作。
4、使用读写锁(read-write lock):读写锁允许多个线程同时读取共享资源,但在写入时只允许一个线程进行。这可以提高程序的并发性能。
5、避免共享数据:如果可能的话,避免多个线程共享数据,这样可以避免很多线程安全问题。如果必须共享数据,可以使用上述方法来保证线程安全。
总之,保证线程安全需要使用同步机制来协调多个线程之间的操作,避免出现竞争条件和数据不一致的问题。
12、什么是死锁?如何避免死锁?
死锁是指两个或多个线程互相持有对方需要的锁,从而陷入了无限等待的状态,无法继续执行下去,也无法释放已经占有的锁,导致程序无法正常运行。
为了避免死锁,可以采取以下措施:
1、避免嵌套锁,尽可能地使用单个锁来保证线程安全。
2、避免多个线程同时竞争多个锁,尽可能地将锁的粒度降低到最小。
3、避免线程之间的相互依赖,尽可能地将各个线程的执行逻辑独立开来,减少锁的竞争。
4、尽量避免使用死锁必然发生的条件,例如同步时不允许线程之间持有多个锁,可以使用 ReentrantLock 来实现。
5、使用定时锁,即在获取锁时设置一个超时时间,如果在规定的时间内无法获取到锁,则放弃获取锁,释放已经占有的锁。
6、使用死锁检测工具,当发现死锁时,自动释放已经占有的锁,从而避免程序陷入死锁状态。
需要注意的是,避免死锁需要从程序设计的角度入手,尽可能地避免出现死锁必然发生的条件,同时也需要在实际运行中对程序进行监控和调试,及时发现和解决死锁问题。
13、如何实现线程同步?有哪些方式可以实现线程同步?
线程同步是指为了保证多个线程在访问共享资源时的正确性而采取的一种机制。在多线程环境中,由于多个线程同时访问共享资源,可能会出现数据不一致、运算错误等问题,因此需要进行线程同步来保证数据的正确性。
实现线程同步的方式有以下几种:
1、Synchronized 关键字:Synchronized 是 Java 中实现线程同步最常用的方式。使用 Synchronized 可以对一个代码块或方法进行同步,保证同一时间只有一个线程访问。Synchronized 将代码块/方法加锁,当一个线程访问该代码块/方法时,其他线程必须等待,直到该线程执行完毕,释放锁之后才能继续访问。
2、ReentrantLock:ReentrantLock 是一个可重入的互斥锁,它也可以用来实现线程同步。与 Synchronized 不同的是,ReentrantLock 可以提供更高的灵活性,例如可中断、公平锁等特性。
3、volatile 关键字:使用 volatile 关键字可以保证变量的可见性和有序性,从而达到线程同步的目的。在多线程环境中,当一个线程修改了 volatile 变量的值,其他线程能够立即看到修改后的值,从而避免了数据不一致的问题。
4、AtomicInteger:AtomicInteger 是一个原子类型的整数,它可以保证对其进行操作的原子性。在多线程环境中,使用 AtomicInteger 可以避免多个线程同时修改同一个变量的问题,从而实现线程同步。
需要注意的是,在使用这些方式实现线程同步时,还需要注意避免死锁、饥饿等问题,同时也需要尽可能地降低锁的粒度,减少线程之间的竞争,从而提高程序的性能。
14、如何实现线程间通信?有哪些方式可以实现线程间通信?
线程间通信是指在多线程环境中,两个或多个线程之间通过共享内存或消息传递等方式进行信息交换和协作的过程。线程间通信可以实现对共享资源的同步访问,从而保证程序的正确性和效率。
实现线程间通信的方式有以下几种:
1、wait() 和 notify():wait() 和 notify() 方法是 Object 类中提供的两个方法,可以实现线程之间的等待和唤醒。当一个线程调用 wait() 方法时,该线程会释放对象锁并进入等待状态,直到被其他线程调用该对象的 notify() 或 notifyAll() 方法唤醒。使用 wait() 和 notify() 可以实现线程之间的协作和同步。
2、Condition:Condition 是 Java 中提供的线程间通信机制,它可以用来代替 wait() 和 notify() 方法。Condition 可以让多个线程等待在同一个锁的不同条件下,从而实现更灵活的线程同步。>3、Semaphore:Semaphore 是一个计数器,可以用来控制同时访问共享资源的线程个数。Semaphore 维护了一个许可证集合,线程可以通过 acquire() 方法获取许可证、执行访问共享资源的操作,然后通过 release() 方法释放许可证,从而使其他线程可以继续访问共享资源。
4、CountDownLatch:CountDownLatch 是一个倒计数器,可以用来控制某个线程等待其他多个线程完成后再执行。CountDownLatch 维护了一个计数器,当计数器的值减为 0 时,等待的线程就会被唤醒,从而可以继续执行。
5、BlockingQueue:BlockingQueue 是一个阻塞队列,可以用来实现线程之间的数据共享和协调。BlockingQueue 提供了 put() 和 take() 两个方法,分别用于插入和获取队列中的元素。当队列为空时,take() 方法会阻塞等待元素的插入,当队列满时,put() 方法会阻塞等待元素的获取。
需要注意的是,在使用这些方式实现线程间通信时,需要合理地设计程序结构,尽可能地降低线程之间的耦合度,从而提高程序的可维护性和可扩展性。同时,还需要注意避免死锁、饥饿等问题,保证程序的正确性和效率。
15、什么是线程池?如何创建线程池?
线程池是一种管理和复用线程资源的机制,它可以避免频繁地创建和销毁线程,从而提高程序的性能和可扩展性。线程池通过维护一个线程队列和一个任务队列,有效地利用了线程资源,可以满足多线程并发执行的需求。
创建线程池的步骤如下:
1、创建一个线程池对象:可以使用 Executors 类提供的静态工厂方法来创建线程池,
例如:
Executors.newFixedThreadPool()、
Executors.newCachedThreadPool()、
Executors.newSingleThreadExecutor() 等。
其中,newFixedThreadPool() 创建一个固定大小的线程池,
newCachedThreadPool() 创建一个可缓存的线程池,
newSingleThreadExecutor() 创建一个只有单个线程的线程池。
2、提交任务到线程池:可以使用 execute() 或 submit() 方法向线程池中提交任务。execute() 方法用于提交 Runnable 类型的任务,submit() 方法用于提交 Callable 类型的任务,它可以返回执行结果。
3、关闭线程池:在不需要使用线程池时,应该及时关闭线程池以释放资源。可以使用 shutdown() 方法来关闭线程池,该方法会等待所有已提交的任务执行完成后再关闭线程池。如果需要立即关闭线程池,则可以使用 shutdownNow() 方法。
下面是一个简单的创建线程池的示例代码:
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
threadPool.execute(new MyTask(i));
}
threadPool.shutdown();
其中,MyTask 是自定义的任务类,它实现了 Runnable 接口,用于执行具体的任务操作。在上面的代码中,创建了一个固定大小为 10 的线程池,并提交了 100 个任务到线程池中执行。执行完毕后,调用了 shutdown() 方法来关闭线程池。
16、如何控制线程的执行顺序?有哪些方式可以控制线程的执行顺序?
控制线程的执行顺序是多线程编程中的一个重要问题,它可以提高程序的可靠性和效率。
以下是几种控制线程执行顺序的方式:
1、join() 方法:join() 方法是 Thread 类中提供的一个方法,它可以让一个线程等待另一个线程执行完成后再继续执行。例如,可以在主线程中调用子线程的 join() 方法,让主线程等待子线程执行完成后再继续执行。
2、synchronized 关键字:synchronized 关键字可以用来实现线程之间的同步,从而控制线程的执行顺序。例如,可以使用 synchronized 关键字同步多个线程访问共享资源的代码块,从而保证它们的代码执行顺序。
3、Lock 接口:Lock 接口是 Java 中提供的一个可重入锁,它可以用来控制线程的执行顺序。例如,使用 Lock 接口实现的 Condition 类可以让线程等待或唤醒其他线程,从而实现更灵活的线程同步。
4、wait() 和 notify() 方法:wait() 和 notify() 方法是 Object 类中提供的两个方法,可以实现线程之间的等待和唤醒。例如,在实现多个线程间的通信时,可以使用 wait() 和 notify() 方法来控制线程的执行顺序。
5、CountDownLatch 类:CountDownLatch 类是一个倒计数器,用于控制多个线程之间的执行顺序。例如,可以将 CountDownLatch 的计数器初始化为 1,让多个线程等待在该对象上,直到某个线程调用了 countDown() 方法,计数器的值减为 0 后,其他线程才能继续执行。
需要注意的是,控制线程的执行顺序需要谨慎地设计程序逻辑,避免死锁和饥饿等问题,从而保证程序的正确性和效率。同时,还需要合理地使用同步机制和线程间通信,以充分利用多核 CPU 的并行能力。
17、如何优化多线程程序?有哪些技巧可以提高多线程程序的性能?
优化多线程程序需要考虑以下几个方面:
1、减小线程切换的开销:可以使用线程池来避免频繁创建和销毁线程,减小线程切换的开销。
2、减小同步的开销:可以使用更高效的同步机制,如使用读写锁来避免对只读的数据进行加锁操作,使用乐观锁来减少锁的粒度等。
3、减少数据传输的开销:可以将数据传输操作转换为局部性原理,将计算移动到数据旁边,避免数据的重复传输。
4、使用高效的线程通信机制:可以使用更高效的线程通信机制,如使用共享内存来传递数据,使用消息队列来传递消息等。
5、合理分配任务:可以将任务合理地分配到不同的线程中执行,以充分利用系统的资源,提高程序的性能。
6、避免线程安全问题:可以使用volatile关键字来保证变量的可见性,使用synchronized关键字来保证变量的原子性等。
7、使用GPU加速计算:如果程序中有大量的计算操作,可以考虑使用GPU加速计算来提高程序的性能。
总之,优化多线程程序需要综合考虑多个方面,选择合适的优化策略,以提高程序的性能。
18、文件 IO 操作的常见方式有哪些?
文件 IO 操作的常见方式有
FileInputStream
和FileOutputStream
、FileReader
和FileWriter
、BufferedInputStream
和BufferedOutputStream
、BufferedReader
和BufferedWriter
等。
19、如何读取文本文件的内容?
可以使用 FileReader 和 BufferedReader 来读取文本文件的内容。其中,FileReader 可以读取字符流,而 BufferedReader 可以提高读取性能和效率。
例如:
FileReader fileReader = new FileReader("file.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
fileReader.close();
20、如何读取二进制文件的内容?
可以使用 FileInputStream 和 BufferedInputStream 来读取二进制文件的内容。其中,FileInputStream 可以读取字节流,而 BufferedInputStream 可以提高读取性能和效率。
例如:
FileInputStream fileInputStream = new FileInputStream("file.bin");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[1024];
int len;
while ((len = bufferedInputStream.read(buffer)) != -1) {
// 处理读取的字节数据
}
bufferedInputStream.close();
fileInputStream.close();
21、如何写入文本文件和二进制文件?
可以使用 FileWriter 和 BufferedWriter 来写入文本文件,使用 FileOutputStream 和 BufferedOutputStream 来写入二进制文件。其中,FileWriter 和 FileOutputStream 可以直接写入字符和字节数据,而 BufferedWriter 和 BufferedOutputStream 可以提高写入性能和效率。
例如:
// 写入文本文件
FileWriter fileWriter = new FileWriter("file.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("Hello, World!");
bufferedWriter.newLine();
bufferedWriter.write("This is a text file.");
bufferedWriter.close();
fileWriter.close();
// 写入二进制文件
FileOutputStream fileOutputStream = new FileOutputStream("file.bin");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] buffer = {0x01, 0x02, 0x03};
bufferedOutputStream.write(buffer);
bufferedOutputStream.close();
fileOutputStream.close();
22、如何解析 JSON 数据?
可以使用第三方库如 Jackson、Gson、Fastjson 等来解析 JSON 数据。
23、MD5 加密的原理是什么?
MD5 是一种消息摘要算法,用于将任意长度的消息压缩成一个长度为 128 位的唯一值,通常用于验证数据的完整性和一致性。
MD5 加密的过程可以简单概括为以下几个步骤:
- 将原始消息划分成若干个块;
- 对每个块进行逐个处理,包括填充、迭代压缩、输出;
- 将最后一个块的输出值作为消息摘要。
24、如何使用 Java 实现 MD5 加密?
可以使用 Java 内置的 MessageDigest 类来实现 MD5 加密。
例如,使用 MessageDigest 类实现字符串的 MD5 加密的示例代码如下:
String input = "Hello, World!";
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(input.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b & 0xff));
}
System.out.println(sb.toString()); // 输出 86fb269d190d2c85f6e0468ceca42a20
25、AES 加密的原理是什么?
AES 是一种对称加密算法,用于将数据加密成不可读的密文。
AES 加密的过程可以简单概括为以下几个步骤:
- 准备加密的数据和密钥;
- 将数据划分成若干个块,并对每个块进行加密;
- 将所有加密后的块组合成密文。
AES 加密使用的密钥长度可以为 128 位、192 位或 256 位,密钥长度越长,加密强度越高,但加密速度越慢。
26、如何使用 Java 实现 AES 加密?
可以使用 Java 内置的 Cipher 类来实现 AES 加密。
例如,使用 Cipher 类实现字符串的 AES 加密和解密的示例代码如下:
String input = "Hello, World!";
String key = "1234567890123456";
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] encrypted = cipher.doFinal(input.getBytes());
System.out.println(Arrays.toString(encrypted)); // 输出加密后的字节数组
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println(new String(decrypted)); // 输出 Hello, World!
27、Lottie 动画和SVG使用
Lottie 是 Airbnb 开源的一款动画渲染框架,它通过将 Adobe After Effects 中的矢量动画转换成 JSON 格式,实现了在移动端和 Web 端实时渲染高质量动画的效果。
Android使用lottie动画的原因有12:
开发无需编写动画,只需加载。
可手动设置进度、绑定手势、事件等。
可网络加载,动态控制播放速度等。
降低动画设计和开发成本。
解决设计提供动画效果与实现不一致问题。
占用空间更小。
不同的手机分辨率不需要适配。
28、Android为什么使用 SVG?
减小app体积。
加载速度快。
放大缩小无拉伸。
SVG描述的是图形的路径,放大缩小后不存在失真问题。
SVG文件是纯粹的XML。
SVG可以动态修改其颜色。
SVG适用于小图标,Android中使用位图,SVG最终使用也是转化为位图,如果图像过大,SVG转化位图的时间也将增加。
网友评论