Android 基础筑基(一)

Java基础

1、重载和重写是什么意思,区别是什么?

中文意思容易混淆,用英文记会清晰一些:Overload与Override。

重载(Overload)

在同一个类中,多个方法名称相同,但参数不同(数量或类型不同),这叫做方法重载。

java 复制代码
class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;
    }
}

重写(Override)

子类中重新定义父类中的方法(方法名、参数都一样),叫做方法重写,目的是修改或扩展父类的行为。

java 复制代码
class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

2、Java中在传参数时是将值进行传递,还是传递引用?

在 Java 中,参数传递机制统一是:

按值传递(Pass by Value)

但这个"值"是变量的值 ,可能是基本类型的值 ,也可能是对象引用的值,这就容易让人混淆。

1. 传递基本类型(int、float、boolean 等)

值的复制,互不影响:

java 复制代码
void change(int x) {
    x = 10;
}

int a = 5;
change(a);
System.out.println(a); // 输出:5(没有变)

2. 传递 对象引用类型(比如传一个 List、数组、对象等)

java 复制代码
void changeName(Person p) {
    p.name = "Alice";      // 改变对象的内容(有效)
    p = new Person("Bob"); // 改变引用本身(无效)
}

Person p1 = new Person("Tom");
changeName(p1);
System.out.println(p1.name); // 输出:Alice,而不是 Bob

分析:

  • p 是对象引用,传进去时复制了引用的值(地址)
  • p.name = "Alice":通过地址改对象内容,有效
  • p = new Person(...):只是改了 p 的副本指向的地址,对外部 p1 没影响,无效

3、sychronied修饰普通方法和静态方法的区别?

js 复制代码
public synchronized void instanceMethod() {
    // 线程安全的实例方法
}

锁的是:当前对象(this)

  • 每个对象有一个对象锁(monitor)
  • 调用该方法时,线程必须先拿到该对象的锁
js 复制代码
public static synchronized void staticMethod() {
    // 线程安全的静态方法
}

锁的是:类对象(Class对象,即 MyClass.class

  • 所有这个类的实例共享一把类锁
  • 和具体哪个对象无关

4、volatile 能否保证线程安全和synchronize有什么区别?

volatile

volatile 是 Java 的一个关键字,用来修饰变量,表示:
当一个线程修改了这个变量的值,其他线程能立即看到最新值(可见性)。

编译器和 CPU 不会重排序它前后的读写操作(有序性)。

总结:

  • 单纯的 volatile 变量的 set(写)和 get(读)操作是线程安全的,能保证可见性和原子性(针对单次赋值)。
  • 多线程读写同一个 volatile 变量,其他线程能立刻看到最新值。
  • 但如果是复合操作(比如 count++),volatile 无法保证原子性,需要用锁或原子类

synchronized

synchronized 是用来给代码块或方法加锁的,表示: 只有获得锁的线程才能执行被保护的代码(互斥访问)

能保证:

  • 原子性(操作不会被中断)
  • 可见性(释放锁前写入的内容,对其他线程可见)
  • 有序性(锁内代码执行有顺序)

5、Android如何判断对象是否可回收,GC会在什么时候触发

Android 如何判断对象是否可回收?

Android(使用 ART 虚拟机)主要采用的是:
可达性分析算法(Reachability Analysis)

判断逻辑:

从一组称为 GC Roots 的对象出发,沿着引用链向下搜索:

  • 如果对象可被 GC Roots 直接或间接引用 → 可达,不可回收
  • 如果对象与 GC Roots 没有引用关系 → 不可达,判定为垃圾,可回收

常见 GC Root 包括:

  • 活跃线程(Thread 对象)
  • 当前方法调用栈中的对象(局部变量)
  • 静态字段引用的对象
  • JNI 中引用的对象(Native 持有 Java 对象)

Android GC 触发时机? GC 并不会一直运行,而是在满足某些条件时被动或主动触发,常见触发条件如下:

场景 描述
内存不足 分配新对象时,如果没有足够空间,就会触发 GC
阈值触发 分配对象数/大小达到阈值
系统空闲时 系统空闲(Idle)状态下,后台触发 GC 降低内存
手动调用 System.gc() 只是建议 GC,不保证立刻执行
内存分析/调试工具 使用内存分析工具时强制触发 GC

6、String、StringBuilder、StringBuffer 有什么区别?

类型 定义
String 不可变的字符串常量,每次修改都会生成新对象。
StringBuilder 可变字符串,适合单线程场景,效率高。
StringBuffer 可变字符串,线程安全(方法加了 synchronized),适合多线程。
js 复制代码
@Override
@NeverInline
public StringBuilder append(boolean b) {
    super.append(b);
    return this;
}
java 复制代码
@Override
synchronized StringBuffer append(AbstractStringBuilder asb) {
    toStringCache = null;
    super.append(asb);
    return this;
}

在大量拼接字符串的场景中的性能:

dart 复制代码
StringBuilder > StringBuffer >> String

因为:

  • String 每次拼接都会创建新对象(低效)
  • StringBuffer 多了同步锁(比 StringBuilder 慢)
  • StringBuilder 无锁,性能最佳(适合绝大多数情况)

7、try-catch-finally 中 finally 一定会执行吗?return 会影响吗?

绝大多数情况下,finally 块一定会执行,无论:

  • 是否抛出异常
  • 是否有 return
  • 是否有 breakcontinue

8、HashMap 的工作原理

js 复制代码
Map<String, String> map = new HashMap<>();
map.put("dog", "旺财");
map.put("cat", "咪咪");
map.put("pig", "佩奇");

内部结构:

js 复制代码
table[0]      →  null
table[1]      →  Entry{key="cat", value="咪咪"}
table[2]      →  Entry{key="dog", value="旺财"} → Entry{key="pig", value="佩奇"} (链表)
table[3]      →  null

每个桶(数组索引)可以容纳多个 Entry,形成链表或树。

步骤详解:

  1. 计算 hash 值
    hash("dog") → 得到一个整数 hash 值

  2. 确定数组索引
    index = hash & (table.length - 1) → 落入某个 table[i]

  3. 查看该位置是否已有元素

    • 若无,直接插入新 Entry
    • 若有,Java 7 及以下永久链表,Java 8起同一桶链表长度 > 8 且 table.length ≥ 64,转为红黑树

Get流程

js 复制代码
map.get("dog")
  1. 计算 "dog" 的 hash → 得到 index
  2. 找到 table[index]
  3. 遍历链表 / 树,判断 key 是否相等(使用 equals())
  4. 找到返回对应的 value

9、Java中的线程池有哪些

线程池 方法 特点 应用场景
固定线程池 Executors.newFixedThreadPool(int n) 固定数量线程 控制并发线程数,线程可复用
单线程池 Executors.newSingleThreadExecutor() 单个线程顺序执行任务 保证顺序、线程串行化
缓存线程池 Executors.newCachedThreadPool() 按需创建线程,空闲线程复用,最多 Integer.MAX_VALUE 个线程 执行大量短期异步任务
定时线程池 Executors.newScheduledThreadPool(int n) 可定时或周期性执行任务 定时任务、周期调度
工作窃取线程池(Java 8+) Executors.newWorkStealingPool() 使用 ForkJoinPool 实现,多个任务队列,自动平衡任务 并行计算、多核利用

大部分是使用了ThreadPoolExecutor:

js 复制代码
ExecutorService threadPool = new ThreadPoolExecutor(
    corePoolSize,          // 核心线程数
    maximumPoolSize,       // 最大线程数
    keepAliveTime,         // 线程存活时间
    TimeUnit.SECONDS,      // 时间单位
    new LinkedBlockingQueue<>(),  // 队列
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

可以结合源码,看看Executors提供的几个线程池,各个参数的区别。

参数 含义
corePoolSize 核心线程数,常驻线程
maximumPoolSize 最大线程数
keepAliveTime 非核心线程闲置多久被回收
workQueue 阻塞队列,保存待执行任务
threadFactory 创建线程的工厂
handler 拒绝策略,如:抛异常、调用者执行、丢弃任务

10、Java 的四种引用类型

特性 强引用 软引用 弱引用 虚引用
是否影响 GC 不回收 低内存时回收 可回收 可回收
回收条件 无引用才回收 内存不足时回收 GC 扫描时回收 GC 后回收并通知
主要用途 一般对象持有 缓存 避免泄漏 跟踪回收、资源释放
是否能 get() 有值 有值 有值 始终 null
需配合 ReferenceQueue 不用 不用 可选 必须

引用的核心作用:

帮助 GC 判断一个对象是否可达,是否应当被回收。

GC 会从一组被称为 "GC Roots" 的对象出发,沿着引用链向下遍历对象图。

  • 如果某个对象能从 GC Roots 通过引用链访问到,说明它不能被回收;
  • 如果无法访问到 ,则认为对象 "不可达" ,可能被回收。
相关推荐
熟悉的新风景2 分钟前
springboot项目或其他项目使用@Test测试项目接口配置-spring-boot-starter-test
java·spring boot·后端
心平愈三千疾2 分钟前
学习秒杀系统-实现秒杀功能(商品列表,商品详情,基本秒杀功能实现,订单详情)
java·分布式·学习
玩代码32 分钟前
备忘录设计模式
java·开发语言·设计模式·备忘录设计模式
BUTCHER51 小时前
Docker镜像使用
java·docker·容器
岁忧1 小时前
(nice!!!)(LeetCode 面试经典 150 题 ) 30. 串联所有单词的子串 (哈希表+字符串+滑动窗口)
java·c++·leetcode·面试·go·散列表
LJianK12 小时前
Java和JavaScript的&&和||
java·javascript·python
RealmElysia2 小时前
java反射
java·开发语言
野蛮人6号3 小时前
黑马点评系列问题之p63unlock.lua不知道怎么整
java·redis·黑马点评
Raners_3 小时前
【Java代码审计(2)】MyBatis XML 注入审计
xml·java·安全·网络安全·mybatis
BillKu3 小时前
Java读取Excel日期内容
java·开发语言·excel