您现在的位置是:网站首页> Android
Java学习笔记
- Android
- 2025-07-05
- 1403人已阅读
Java语法回顾
包也就是目录(import com.hello.A 相当于目录com\\hello\\A)
synchronized 是 Java 中的一个关键字,用于实现线程同步
数据类型转换
long i=122;
short c=(short)i;
一、数字转字符串
1、通过ToString() 方法, Double 就是一个包装类,String s1 = Double.toString(num);
Integer同理
2、通过valueof() 方法, 本质上还是调用 toString() 方法,String s2 = String.valueOf(num);
3、技巧性,第三种没有借助包装类 String s3 =""+num;
二、字符串转数字
string 和int之间的转换
string转换成int :Integer.valueOf("12") 或者Integer.PaseInt(“12”)
三、 char和int之间的转换
首先将char转换成string
String str=String.valueOf('2')
Integer.valueof(str) 或者Integer.parseInt(str)
Integer.valueof返回的是Integer对象,Integer.parseInt
返回的是int
四、Java String转byte[]
在 Java 中,将 String 转换为 byte [] 需要指定字符编码,因为不同的编码会导致不同的字节表示。以下是几种常见的转换方式及其示例:
1. 使用getBytes()方法(最常用)
java
String str = "Hello, 世界!";
// 使用平台默认编码(不推荐,可能导致跨平台问题)
byte[] bytes1 = str.getBytes();
// 推荐:显式指定UTF-8编码
byte[] bytes2 = str.getBytes(java.nio.charset.StandardCharsets.UTF_8);
// 或简写为
byte[] bytes3 = str.getBytes("UTF-8");
注意事项:
默认编码:getBytes()若无参数,会使用 JVM 的默认编码(如 Windows 上可能是 GBK,Linux/macOS 上通常是 UTF-8),可能导致数据不一致。
推荐编码:优先使用UTF-8,它支持全球所有字符,且是互联网标准。
2. 处理字符编码异常
当指定的编码名称无效时,getBytes(String charsetName)会抛出UnsupportedEncodingException(受检异常),需显式处理:
java
try {
byte[] bytes = str.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// 理论上UTF-8不会抛出此异常,但其他编码(如"GBK")可能需要处理
e.printStackTrace();
}
简化方案:使用 Java 7 + 的标准编码枚举(无需异常处理):
java
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
3. 不同编码的字节差异
同一字符串在不同编码下的字节数组可能不同:
java
String str = "中文";
byte[] utf8Bytes = str.getBytes("UTF-8"); // 每个中文占3字节
byte[] gbkBytes = str.getBytes("GBK"); // 每个中文占2字节
System.out.println(Arrays.toString(utf8Bytes)); // [-28, -72, -83, -26, -106, -121]
System.out.println(Arrays.toString(gbkBytes)); // [-42, -48, -50, -60]
4. Base64 编码转换(特殊场景)
若需将字节数组转换为可打印的字符串(如网络传输),可结合 Base64 编码:
java
import java.util.Base64;
String original = "Hello, 世界!";
// 字符串 → 字节数组 → Base64编码字符串
String base64Str = Base64.getEncoder().encodeToString(original.getBytes("UTF-8"));
// 解码:Base64字符串 → 字节数组 → 原始字符串
byte[] decodedBytes = Base64.getDecoder().decode(base64Str);
String restored = new String(decodedBytes, "UTF-8");
Java的位操作
在 Java 里,位操作直接对二进制位进行运算,其操作数是整数类型(像int、long、short、byte)。位操作执行效率颇高,常被用于底层编程、数据压缩以及优化场景。下面为你介绍 Java 里的位运算符及其示例:
按位与运算符(&)
只有当两个操作数的对应位都为 1 时,结果的该位才是 1,否则为 0。
java
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
int result = a & b; // 二进制:0001 → 十进制:1
System.out.println(result); // 输出:1
按位或运算符(|)
只要两个操作数的对应位中有一个为 1,结果的该位就是 1,只有当对应位都为 0 时结果位才为 0。
java
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
int result = a | b; // 二进制:0111 → 十进制:7
System.out.println(result); // 输出:7
按位异或运算符(^)
当两个操作数的对应位不同时,结果的该位为 1,相同时结果位为 0。
java
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
int result = a ^ b; // 二进制:0110 → 十进制:6
System.out.println(result); // 输出:6
按位取反运算符(~)
它会将操作数的每一位进行取反,也就是把 1 变成 0,把 0 变成 1。
java
int a = 5; // 二进制:0101
int result = ~a; // 二进制:1010(补码形式)→ 十进制:-6
System.out.println(result); // 输出:-6
左移运算符(<<)
将左侧操作数的二进制位向左移动右侧操作数指定的位数,右边空出的位用 0 填补。
java
int a = 5; // 二进制:0101
int result = a << 2; // 二进制:010100 → 十进制:20
System.out.println(result); // 输出:20
右移运算符(>>)
把左侧操作数的二进制位向右移动右侧操作数指定的位数,左边空出的位用符号位填充(若为正数则补 0,若为负数则补 1)。
java
int a = -5; // 二进制补码:11111011
int result = a >> 2; // 二进制:11111110 → 十进制:-2
System.out.println(result); // 输出:-2
无符号右移运算符(>>>)
将左侧操作数的二进制位向右移动右侧操作数指定的位数,不管是正数还是负数,左边空出的位都用 0 填充。
java
int a = -5; // 二进制补码:11111011
int result = a >>> 2; // 二进制:00111111111111111111111111111110 → 十进制:1073741822
System.out.println(result); // 输出:1073741822
Java内存byte[] 复制操作
在 Java 里,对byte[]进行复制操作有多种方式,下面为你介绍常见的方法及其适用场景:
1. 使用for循环逐个元素复制
java
byte[] source = {1, 2, 3, 4, 5};
byte[] dest = new byte[source.length];
for (int i = 0; i < source.length; i++) {
dest[i] = source[i];
}
优点:实现简单直观。
缺点:复制效率较低,尤其是当数组规模较大时。
适用场景:适用于小规模数组的复制,或者在需要对复制过程进行自定义处理的情况下使用。
2. 利用System.arraycopy()方法(推荐)
java
byte[] source = {1, 2, 3, 4, 5};
byte[] dest = new byte[source.length];
System.arraycopy(source, 0, dest, 0, source.length);
优点:这是一个本地方法,复制效率高。
缺点:需要手动创建目标数组。
适用场景:适用于大多数需要高效复制数组的场景。
3. 借助Arrays.copyOf()方法
java
byte[] source = {1, 2, 3, 4, 5};
byte[] dest = Arrays.copyOf(source, source.length);
优点:代码简洁,能够自动创建目标数组。
缺点:本质上还是调用了System.arraycopy()方法。
适用场景:适用于需要快速复制数组并自动处理数组创建的场景。
4. 使用clone()方法
java
byte[] source = {1, 2, 3, 4, 5};
byte[] dest = source.clone();
优点:代码最为简洁。
缺点:这是浅拷贝,不过对于byte[]这种基本类型数组来说,效果等同于深拷贝。
适用场景:适用于需要快速复制数组的场景。
5. 使用ByteBuffer类
java
byte[] source = {1, 2, 3, 4, 5};
byte[] dest = new byte[source.length];
ByteBuffer srcBuffer = ByteBuffer.wrap(source);
ByteBuffer destBuffer = ByteBuffer.wrap(dest);
destBuffer.put(srcBuffer);
优点:适合用于处理缓冲区的场景,并且支持链式操作。
缺点:对于简单的数组复制来说,使用这种方法显得比较繁琐。
适用场景:适用于 NIO 编程或者需要进行缓冲区操作的场景。
类和接口
【修饰】 class 类名 【extends 父类】【implements 接口名】
{
类成员变量声明
类方法声明
}
修饰 protected 它只能被与该类处于同一个包的类或该类的子类直接存取和使用
定义了 abstract抽象方法的类必须被声明为abstract的抽象类
调用父类函数如:
public void MyFunc()
{
super.MyFunc();
...
}
调用父类的构造函数
super(a,b,c);
调用另一个构造函数
this(a,b,c);
在java这门编程语言中,final是一个关键字,它可以被用来修饰类,变量以及成员方法
被final修饰的变量,又叫被称为 自定义常量
1. final修饰类
当final关键字修饰一个类,则该类会成为最终类,即该类不能被继承(俗称“断子绝孙类”),但是该类可以有父类
//类名为Fu的类被final关键字修饰,代表其不能被继承
final class Fu {
}
//现在类名为Zi的类想继承Fu这个类,编译器会报错
class Zi extends Fu {
}
2. final修饰变量(成员变量和局部变量)
变量分为成员变量和局部变量,他们被final修饰时有不同的注意事项。
(1) final修饰成员变量:该成员变量必须在其所在类对象创建之前被初始化(且只能被初始化一次)。
这句话的意思是: 被final修饰的成员变量,一定要被赋值且只能被赋值一次,且必须是在这个成员变量所在的类对象创建之前被赋值。
试验代码:
第一种,给其直接初始化(赋值):
private final int a=5
第二种,在构造代码块中将其初始化(赋值):
private final int a;
//构造函数
{
a=5;
}
第三种,在成员变量所在类的构造方法中将其初始化(赋值):
private final int a;
public Demo(int b)
{
this.a=b;
}
总结:
final修饰成员变量,该变量必须在 其所在类对象 创建之前完成初始化且只能被初始化一次(我的理解:对象被创建,说明要使用这个对象,所以身为这个对象属性之一的成员变量就必须要被赋值)
final修饰局部变量,该变量在定义时可以不被初始化,但是使用之前,必须完成初始化且只能初始化一次!
总而言之一句话:
final修饰的成员变量在定义时必须初始化(三种方法),final修饰的局部变量定义时可以不被初始化,但是使用之前必须完成初始化!
当final修饰引用数据类型(类、接口、数组)的变量,则引用变量所指向的对象(即该变量所存放的地址值)不能更改,但是该对象的内容(即地址值上存储的内容)可以更改!!
不能改变被final修饰的引用变量所指向的对象,但是可以改变其指向对象的内容:
final修饰成员方法
当final关键字修饰了成员方法,则意味着这个方法不能被重写,但是可以被继承(注意,这里final修饰的是方法而不是类
class C{
public final void Test(){
}
}
class D extends C{
//错误会报错
public void test(){
}
}
抽象类与抽象方法
首先创建一个表示图形的抽象类 Shape,代码如下所示
public abstract class Shape {
public int width; // 几何图形的长
public int height; // 几何图形的宽
public Shape(int width, int height) {
this.width = width;
this.height = height;
}
public abstract double area(); // 定义抽象方法,计算面积
}
定义一个正方形类,该类继承自形状类 Shape,并重写了 area( ) 抽象方法。正方形类的代码如下:
public class Square extends Shape {
public Square(int width, int height) {
super(width, height);
}
// 重写父类中的抽象方法,实现计算正方形面积的功能
@Override
public double area() {
return width * height;
}
}
定义一个三角形类,该类与正方形类一样,需要继承形状类 Shape,并重写父类中的抽象方法 area()。三角形类的代码实现如下:
public class Triangle extends Shape {
public Triangle(int width, int height) {
super(width, height);
}
// 重写父类中的抽象方法,实现计算三角形面积的功能
@Override
public double area() {
return 0.5 * width * height;
}
}
最后创建一个测试类,分别创建正方形类和三角形类的对象,并调用各类中的 area() 方法,打印出不同形状的几何图形的面积。测试类的代码如下:
public class ShapeTest {
public static void main(String[] args) {
Square square = new Square(5, 4); // 创建正方形类对象
System.out.println("正方形的面积为:" + square.area());
Triangle triangle = new Triangle(2, 5); // 创建三角形类对象
System.out.println("三角形的面积为:" + triangle.area());
}
}
在该程序中,创建了 4 个类,分别为图形类 Shape、正方形类 Square、三角形类 Triangle 和测试类 ShapeTest。其中图形类 Shape 是一个抽象类,创建了两个属性,分别为图形的长度和宽度,并通过构造方法 Shape( ) 给这两个属性赋值。
在 Shape 类的最后定义了一个抽象方法 area( ),用来计算图形的面积。在这里,Shape 类只是定义了计算图形面积的方法,而对于如何计算并没有任何限制。也可以这样理解,抽象类 Shape 仅定义了子类的一般形式。
正方形类 Square 继承抽象类 Shape,并实现了抽象方法 area( )。三角形类 Triangle 的实现和正方形类相同,这里不再介绍。
在测试类 ShapeTest 的 main( ) 方法中,首先创建了正方形类和三角形类的实例化对象 square 和 triangle,然后分别调用 area( ) 方法实现了面积的计算功能。
抽象类使用注意事项
一个类如果定义为抽象类,那里面可以没有抽象方法
一个类中如果有抽象方法,那所在类必为抽象类
抽象类不能被实例化,可以实例化这个抽象类的非抽象子类,
抽象类中的所有非抽象子类必须重写抽象类中的抽象方法
抽象类中,可以有构造方法,是供给子类创建对象时,初始化父类成员使用的
抽象类中的抽象方法只是声明,不包含方法体,也就是不给出具体的实现细节
接口
public interface Shape{
public abstract double area();
public abstract double valume();
public abstract String getName();
}
public class MyClass implements Shape{
public double area()
{
return 0.0;
}
}
Java类接口例子
基类继承
public abstract class Person
{
private String name;
public Person(String n)
{
name=n;
}
public abstract String getDes();
public String getName()
{
return name;
}
}
class Student extends Person
{
public Student(String n,String m)
{
super(n);
major=m;
}
public String getDec()
{
return major;
}
private String major;
}
接口
public interface Flayable{
public final int wiNumber;
public abstract void Fly();
}
public class Bat implements Fly
{
@override
public void Fly()
{
}
}
Java泛类型
public class UHFReaderResult<T> {
/**
* 结果码类
*/
public class ResultCode{
/*
* 成功
* */
public static final int CODE_SUCCESS=0;
/*
* 失败
* */
public static final int CODE_FAILURE=1;
/*
* 未连接读写器
* */
public static final int CODE_READER_NOT_CONNECTED=2;
/*
* 打开串口失败
* */
public static final int CODE_OPEN_SERIAL_PORT_FAILURE=3;
/*
* 上电失败
* */
public static final int CODE_POWER_ON_FAILURE=4;
}
/**
* 返回的消息类
*/
public class ResultMessage{
//uhf读写器没有连接.
public static final String READER_NOT_CONNECTED="没有连接UHF模块.";
public static final String OPEN_SERIAL_PORT_FAILURE="打开串口失败.";
public static final String CODE_POWER_ON_FAILURE="模块上电失败!";
}
public UHFReaderResult(int resultCode){
this.resultCode=resultCode;
}
public UHFReaderResult(int resultCode,String message){
this.resultCode=resultCode;
this.message=message;
}
public UHFReaderResult(int resultCode,String msg,T data){
this.resultCode=resultCode;
this.message=message;
this.data=data;
}
/*
* 返回结果
* */
private int resultCode;
/*
* 返回结果
* */
private T data;
/*
* 消息
* */
private String message;
/**
* 获取结果码
*
* @return uhf操作返回的结果码,参考ResultCode里面常量值
*
* @see ResultCode
*/
public int getResultCode() {
return resultCode;
}
public void setResultCode(int resultCode) {
this.resultCode = resultCode;
}
/**
* 获取结果消息
*
* @return 结果消息
*
*/
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
/**
* 获取结果数据
*
* @return 结果数据
*
*/
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
synchronized 是 Java 中的一个关键字,用于实现线程同步
synchronized 是 Java 中的一个关键字,用于实现线程同步,确保在同一时刻只有一个线程可以执行被标记的代码块或方法。在你提供的代码中,synchronized 修饰了 connect 方法,这意味着:
1.线程互斥访问:如果多个线程同时调用同一个 UHFReader 实例的 connect 方法,它们将被强制串行执行。也就是说,当一个线程正在执行该方法时,其他线程必须等待,直到当前线程执行完毕。
2.锁的作用对象:synchronized 方法默认使用对象实例(即 this)作为锁。因此,如果有多个线程访问同一个对象的 connect 方法,它们会竞争同一把锁;但如果是不同对象的 connect 方法调用,则不会相互影响。
3.原子性保障:在多线程环境中,connect 方法内部的操作会被视为一个不可分割的原子操作,避免多个线程同时修改共享资源导致的数据不一致问题。
典型使用场景
资源独占:例如设备连接(如示例中的 UHF 阅读器连接),防止多个线程同时操作同一设备导致冲突。
初始化保护:确保对象在初始化过程中不会被其他线程干扰。
注意事项
性能影响:频繁的同步可能导致线程阻塞,降低系统吞吐量。
锁粒度:应尽量缩小同步范围(例如使用 synchronized 代码块而非整个方法)。
静态方法同步:如果 connect 是静态方法,锁对象将是类的 Class 对象(即 UHFReader.class),而非实例。
示例对比
java
// 方法级同步(使用 this 作为锁)
public synchronized UHFReaderResult<Boolean> connect(Context context) {
// 同一时刻仅允许一个线程执行此代码
}
// 等价于以下代码块同步
public UHFReaderResult<Boolean> connect(Context context) {
synchronized (this) {
// 同步代码块
}
}
如果需要更具体的解释或示例,可以提供更多上下文。
Java线程同步有哪些方法,具体介绍并给出例子
在 Java 中,线程同步是保证多线程环境下数据一致性和正确性的重要机制。以下是几种常见的线程同步方法及其示例:
1. synchronized 关键字
作用:修饰方法或代码块,确保同一时刻只有一个线程访问。
锁对象:
实例方法:锁是当前对象实例(this)。
静态方法:锁是类的Class对象(如MyClass.class)。
代码块:指定锁对象(如synchronized(obj))。
示例:
java
public class Counter {
private int count = 0;
// 方法级同步(锁为this)
public synchronized void increment() {
count++;
}
// 等价的代码块同步
public void decrement() {
synchronized (this) {
count--;
}
}
}
2. ReentrantLock 类(java.util.concurrent.locks)
作用:显式锁,功能比synchronized更灵活(可中断、可定时、公平锁)。
核心方法:
lock():获取锁。
unlock():释放锁(必须在finally块中调用)。
示例:
java
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 确保锁释放
}
}
}
3. volatile 关键字
作用:保证变量的可见性(一个线程修改后,其他线程立即看到最新值),但不保证原子性。
适用场景:标记状态变量(如isRunning)。
示例:
java
public class FlagExample {
private volatile boolean isRunning = true;
public void stop() {
isRunning = false;
}
public void run() {
while (isRunning) {
// 执行任务
}
}
}
4. Atomic 原子类(java.util.concurrent.atomic)
作用:基于 CAS(Compare-and-Swap)实现无锁原子操作,性能优于synchronized。
常见类:AtomicInteger、AtomicLong、AtomicReference。
示例:
java
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 原子自增
}
}
5. wait()、notify() 和 notifyAll()
作用:线程间通信机制,必须在synchronized块中调用。
wait():释放锁并阻塞线程。
notify():唤醒一个等待的线程。
notifyAll():唤醒所有等待的线程。
示例:生产者 - 消费者模型
java
public class MessageQueue {
private final Object lock = new Object();
private String message;
private boolean available = false;
public void produce(String msg) {
synchronized (lock) {
while (available) {
try {
lock.wait(); // 等待消费者取走消息
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
message = msg;
available = true;
lock.notifyAll(); // 通知消费者消息可用
}
}
public String consume() {
synchronized (lock) {
while (!available) {
try {
lock.wait(); // 等待生产者发送消息
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
available = false;
lock.notifyAll(); // 通知生产者可以继续生产
return message;
}
}
}
6. CountDownLatch
作用:让一个或多个线程等待其他线程完成操作后再继续执行。
核心方法:
countDown():计数器减 1。
await():阻塞直到计数器为 0。
示例:
java
import java.util.concurrent.CountDownLatch;
public class Demo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 等待3个任务
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("任务完成");
latch.countDown();
}).start();
}
latch.await(); // 主线程等待所有任务完成
System.out.println("所有任务已完成");
}
}
7. CyclicBarrier
作用:让一组线程相互等待,直到所有线程都到达某个屏障点后再继续执行。
核心方法:await()。
示例:
java
import java.util.concurrent.CyclicBarrier;
public class Demo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程已就绪,开始执行");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println("线程准备就绪");
barrier.await(); // 等待其他线程
System.out.println("线程开始执行");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
8. Semaphore
作用:控制同时访问某个资源的线程数量。
核心方法:
acquire():获取许可(如果没有则阻塞)。
release():释放许可。
示例:限制同时访问的线程数
java
import java.util.concurrent.Semaphore;
public class ResourceAccess {
private final Semaphore semaphore = new Semaphore(2); // 允许2个线程同时访问
public void accessResource() {
try {
semaphore.acquire();
System.out.println("获取资源访问权");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
System.out.println("释放资源访问权");
}
}
}
总结
同步方法 适用场景 特性
synchronized 简单的方法 / 代码块同步 隐式锁,自动释放
ReentrantLock 复杂场景(可中断、公平锁)显式锁,需手动释放
volatile 变量可见性 不保证原子性
Atomic 类 原子操作(如计数) 无锁,高性能
wait()/notify() 线程间协作 必须在同步块中使用
CountDownLatch 等待多个线程完成 一次性使用
CyclicBarrier 一组线程相互等待 可循环使用
Semaphore 限制并发线程数 控制许可数量
根据具体需求选择合适的同步机制,可以避免线程安全问题并提高程序性能。
上一篇:Android快速回顾