当前位置: 首页 > news >正文

java复习 20

学习通上的练习题再仔细过一遍所有的选项,做好整理总结,相信自己,加油!

1 double c = 0.0f;这样写合法的,是小范围转大范围

合法,但可能存在精度损失风险。以下是详细分析:

1. 语法合法性

  • 合法float 可自动转换为 double(小范围 → 大范围,自动类型提升)。

    java

    double c = 0.0f;  // ✅ 合法,float 转 double 无需显式强制类型转换
    
  • 内存表示

    • float(32 位,单精度) → double(64 位,双精度),转换后精度更高。

2. 潜在风险:精度损失(反向操作时)

  • double → float 需强制转换(可能丢失精度):

    java

    float f = 3.1415926;  // ❌ 编译错误:可能损失精度
    float f = (float) 3.1415926;  // ✅ 强制转换,截断为 3.1415927(float 精度限制)
    
  • 示例验证

    java

    double d = 1.1234567890123456789;
    float f = (float) d;  // f 实际值:1.1234568(精度丢失)
    

3. 常见混淆点

(1)字面量后缀问题
  • 0.0 默认是 double,需加 f 显式声明为 float

    java

    float f = 0.0;  // ❌ 编译错误:double 无法自动转为 float
    float f = 0.0f; // ✅ 正确
    
(2)浮点数比较陷阱
  • float 和 double 比较可能不等(即使数值看似相同):

    java

    double d = 0.1;
    float f = 0.1f;
    System.out.println(d == f);  // ❌ 输出 false(二进制表示不同)
    

4. 速记口诀

“小转大自动,大转小强转;float 转 double 稳,double 转 float 截断!”

总结

  • double c = 0.0f; 合法(自动类型提升)。
  • 但需注意:反向转换(double → float)必须强制类型转换,且可能丢失精度。

2 构造方法没有返回类型的,再强化一下,注意!

在 Java 里,构造方法确实没有返回类型,连 void 都不能写 ,这是它的核心特性之一,展开讲讲:

1. 构造方法的定义规则

构造方法是类里特殊的方法,格式上:

  • 方法名:必须和类名完全一致(大小写都要相同,比如类叫 Person,构造方法就叫 Person)。
  • 返回类型没有返回类型声明(既不能写 void,也不能写 intString 这些返回类型 )。
  • 调用时机:创建对象时(new 关键字)自动调用,没法像普通方法一样手动调用。

举个例子:

java

public class Person {String name;// 构造方法,没有返回类型public Person(String n) { name = n;}
}

创建对象时会触发构造方法:

java

Person p = new Person("张三"); // new 的时候,调用构造方法初始化

2. 为啥不能有返回类型

构造方法的 “隐藏任务” 是创建并初始化对象,JVM 会隐式返回刚创建好的对象引用。从设计逻辑上:

  • 它的核心是 “初始化对象”,不是 “返回一个值”,所以不需要显式声明返回类型。
  • 如果写了 void 或者其他返回类型,Java 就会把它当成普通方法,而不是构造方法,会引发各种问题(比如创建对象时找不到构造方法)。

3. 和普通方法的关键区别

对比项构造方法普通方法
方法名必须和类名一致自定义(符合命名规范即可)
返回类型无(连 void 都不写)必须声明(void 或具体类型)
调用时机new 对象时自动调用对象创建后,手动调用
作用初始化对象属性实现业务逻辑、返回数据等

比如下面代码,Person 里的 Person 是构造方法(无返回类型),sayHi 是普通方法(有 void 返回类型):

java

public class Person {String name;// 构造方法public Person(String n) { name = n;}// 普通方法public void sayHi() { System.out.println("你好,我是" + name);}
}

4. 特殊情况:编译器自动生成构造方法

  • 如果你没写任何构造方法,编译器会自动生成一个 “无参、空实现” 的构造方法(默认构造方法)。
  • 但只要你写了任意一个构造方法(不管有参还是无参),编译器就不会再自动生成默认构造方法。所以如果需要无参构造,建议手动写上,避免后续调用报错。

总结一下:构造方法的标志性特点就是 **“与类同名、无返回类型、创建对象时自动调用”** ,记住这几条,遇到相关题目就能快速判断啦~

“超类”(Superclass)就是 “父类”

在 Java 的继承体系里,“超类”(Superclass)就是 “父类”,指被其他类继承的类。理解这个概念对掌握构造方法的执行顺序很关键,帮你快速理清:

1. 超类(父类)的定义

  • 超类:被继承的类,也叫 “父类” 或 “基类”。
  • 子类:继承超类的类,也叫 “派生类”。
  • 语法:用 extends 关键字声明继承关系。

示例

java

// 超类(父类)
class Animal {public Animal() {System.out.println("Animal 构造方法");}
}// 子类(继承自 Animal)
class Dog extends Animal {public Dog() {System.out.println("Dog 构造方法");}
}

2. 超类构造方法的特性

  • 必须先调用超类构造方法
    子类对象创建时,会自动先调用父类的构造方法,再执行子类自己的构造方法(无论父类构造方法是否为空)。
  • 隐式调用 super()
    子类构造方法的第一行默认有 super()(无参构造),除非显式调用父类的其他构造方法(用 super(参数))。

执行流程

java

Dog dog = new Dog();  // 创建子类对象// 输出结果:
// Animal 构造方法  ← 先执行父类构造
// Dog 构造方法    ← 再执行子类构造

3. 子类显式调用父类构造方法

如果父类没有无参构造方法,子类必须在构造方法中显式调用父类的有参构造(用 super(参数)):

java

class Animal {private String name;public Animal(String name) {  // 父类只有有参构造this.name = name;}
}class Dog extends Animal {public Dog(String name) {super(name);  // 显式调用父类有参构造(必须放第一行!)}
}

4. 关键规则总结

场景规则
父类有无参构造子类构造默认调用 super()(可省略不写)。
父类只有有参构造子类必须在构造方法中用 super(参数) 显式调用父类构造(否则编译报错)。
子类构造中调用 this()this() 和 super() 不能同时存在,因为都必须放在第一行。

5. 常见面试题:构造方法执行顺序

java

class Grandfather {public Grandfather() { System.out.println("爷爷"); }
}class Father extends Grandfather {public Father() { System.out.println("父亲"); }
}class Son extends Father {public Son() { System.out.println("儿子"); }
}// 执行:
new Son();// 输出顺序:
// 爷爷 → 父亲 → 儿子  ← 从最顶层父类依次向下执行

总结

  • 超类 = 父类,是被继承的类。
  • 子类构造必调父类构造(通过 super() 或 super(参数)),且必须放在第一行。
  • 执行顺序:从最顶层父类开始,逐层向下执行,最后执行子类自己的构造方法。

记住这个逻辑,考试遇到继承和构造方法的题,直接按 “先父后子” 顺序推导~ 😄

4 写成Animal dog = new Dog ();这个算什么?(多态,向上转型)

这行代码涉及 Java 中的 多态(Polymorphism) 特性,属于向上转型(Upcasting),具体解释如下:

1. 代码本质:多态的体现

java

Animal dog = new Dog();  // 父类引用指向子类对象
  • 类型分析

    • 编译时类型Animal(引用类型为父类)。
    • 运行时类型Dog(实际对象是子类)。
  • 关键规则

    • 只能调用父类声明的方法(编译时检查)。
    • 实际执行子类重写的方法(运行时动态绑定)。

2. 方法调用规则

假设 Animal 和 Dog 定义如下:

java

class Animal {public void eat() {System.out.println("Animal eats");}
}class Dog extends Animal {@Overridepublic void eat() {  // 重写父类方法System.out.println("Dog eats bones");}public void bark() {  // 子类特有方法System.out.println("Dog barks");}
}
  • 调用父类方法(正常执行):

    java

    Animal dog = new Dog();
    dog.eat();  // 输出:Dog eats bones  ← 运行时调用子类重写的方法
    
  • 调用子类特有方法(编译错误):

    java

    dog.bark();  // ❌ 编译错误:Animal 类型没有 bark() 方法
    

3. 向上转型 vs 向下转型

类型转换语法特点
向上转型父类 引用 = new 子类()自动发生,安全子类是父类的 “一种”)。只能调用父类方法。
向下转型子类 引用 = (子类) 父类引用需强制类型转换,可能抛出 ClassCastException(需先通过 instanceof 检查)。

示例:向下转型

java

Animal animal = new Dog();  // 向上转型(自动)
Dog dog = (Dog) animal;     // 向下转型(需强制)
dog.bark();  // ✅ 编译通过,运行正常

错误案例:

java

Animal animal = new Animal();  // 父类对象
Dog dog = (Dog) animal;        // ❌ 运行时抛出 ClassCastException

4. 多态的核心作用

  • 解耦代码:通过父类引用接收不同子类对象,实现方法调用的灵活性。
  • 统一接口:例如:

    java

    List<String> list = new ArrayList<>();  // List 是接口,ArrayList 是实现类
    list.add("hello");  // 调用接口方法,实际执行 ArrayList 的实现
    

总结

  • Animal dog = new Dog(); 是向上转型,属于多态的典型应用。
  • 编译时限制:只能调用父类中定义的方法。
  • 运行时动态绑定:实际执行子类重写的方法(若有)。

记住这个规则,考试遇到类似题直接按 “编译看左边,运行看右边” 的逻辑分析~ 😄

“编译看左边,运行看右边”的详细解释

在 Animal dog = new Dog(); 这种多态场景下,方法调用的规则可以总结为 “编译看左边,运行看右边”,具体到方法的使用规则如下:

1. 编译阶段:由引用类型(左边)决定可调用的方法

  • 引用类型是父类(Animal,因此只能调用父类中声明的方法(包括父类自身的方法和被子类重写的方法)。
  • 子类特有的方法(如 Dog.bark())无法通过父类引用调用,编译时会报错。

示例

java

Animal dog = new Dog();
dog.eat();       // ✅ 父类有 eat() 方法,编译通过
dog.bark();      // ❌ 父类没有 bark() 方法,编译报错

2. 运行阶段:由实际对象类型(右边)决定方法的具体实现

  • 若子类重写了父类方法,运行时优先执行子类的实现;
  • 若子类未重写父类方法,则执行父类的原始方法。

示例

java

class Animal {public void eat() { System.out.println("Animal eats"); }
}class Dog extends Animal {@Overridepublic void eat() { System.out.println("Dog eats bones"); }
}Animal dog = new Dog();
dog.eat();  // 输出:Dog eats bones  ← 运行时调用子类重写的方法

3. 如何调用子类特有的方法?(向下转型)

若需要调用子类特有的方法(如 Dog.bark()),需先将父类引用强制转换为子类类型(向下转型),但需注意:

  1. 转换前必须用 instanceof 检查类型,避免 ClassCastException
  2. 仅当父类引用实际指向子类对象时,转换才会成功。

示例

java

Animal animal = new Dog();  // 父类引用指向子类对象if (animal instanceof Dog) {  // 先检查类型Dog dog = (Dog) animal;   // 强制转换为子类类型dog.bark();              // ✅ 调用子类特有方法
}

4. 总结:方法调用的完整逻辑

  1. 编译阶段
    • 检查父类(引用类型)是否声明了该方法,未声明则编译报错。
  2. 运行阶段
    • 若子类重写了方法,执行子类实现;
    • 否则,执行父类原始实现。

口诀记忆

  • 可调用的方法看左边(父类)
  • 方法的具体行为看右边(实际对象)

5. 典型场景:多态的应用

多态常用于接口抽象或父类抽象,例如:

java

// 定义形状接口
interface Shape {void draw();
}// 子类实现
class Circle implements Shape {@Override public void draw() { System.out.println("画圆"); }
}class Rectangle implements Shape {@Override public void draw() { System.out.println("画矩形"); }
}// 多态应用:通过接口引用处理不同形状
public void processShape(Shape shape) {shape.draw();  // 编译时检查 Shape 接口是否有 draw(),运行时调用具体实现
}

这种方式让代码更灵活,无需关心具体是哪种形状,只需调用统一接口。

核心结论

  • new Dog() 创建的对象可以通过父类引用(Animal)调用父类声明的方法,具体执行子类重写的逻辑(若存在);
  • 子类特有的方法必须通过向下转型为子类类型后才能调用。

掌握 “编译看左边,运行看右边” 的逻辑,就能轻松处理多态场景下的方法调用问题啦~ 😊

6 考试中常考的多态 核心考点总结

一、多态的定义与本质
  1. 定义:同一操作作用于不同对象,产生不同的执行结果(即 “一个接口,多种实现”)。
  2. 本质:通过父类 / 接口引用调用子类重写的方法,运行时动态绑定实际对象类型。
  3. 核心条件需同时满足):
    • 继承 / 实现关系(子类继承父类或实现接口)。
    • 方法重写(子类覆盖父类的方法)。
    • 父类引用指向子类对象(如 Parent obj = new Child();)。
二、多态的表现形式(必考点)
  1. 继承中的方法重写
    • 父类引用调用子类重写的方法,例如:

      java

      Animal animal = new Dog();  // Animal 是父类,Dog 是子类  
      animal.bark();  // 调用 Dog 重写的 bark() 方法,而非 Animal 的方法  
      
  2. 接口与实现类
    • 接口引用指向实现类对象,例如:

      java

      Comparator<Person> comparator = new AgeComparator();  // Comparator 是接口  
      
  3. 抽象类与子类
    • 抽象类引用指向具体子类对象,例如:

      java

      Shape shape = new Circle();  // Shape 是抽象类,Circle 是子类  
      
三、多态的成员访问规则(易混点)
访问类型编译期行为运行期行为
方法调用检查父类是否存在该方法调用子类重写的方法(动态绑定)
成员变量访问仅访问父类的成员变量与编译期一致(不体现多态)
静态方法调用调用父类的静态方法与编译期一致(不体现多态)

示例

java

class Parent {int num = 10;void method() { System.out.println("Parent method"); }static void staticMethod() { System.out.println("Parent static"); }
}
class Child extends Parent {int num = 20;@Override void method() { System.out.println("Child method"); }static void staticMethod() { System.out.println("Child static"); }
}
Parent p = new Child();
System.out.println(p.num);      // 输出 10(父类变量)
p.method();                     // 输出 "Child method"(子类方法)
p.staticMethod();               // 输出 "Parent static"(父类静态方法)
四、多态的典型应用场景(简答题高频)
  1. 接口回调:如 Java 的 Comparator 接口用于排序(见前例)。
  2. 策略模式:不同支付方式、算法动态切换(如支付策略选择)。
  3. 模板方法模式:父类定义流程骨架,子类实现细节(如游戏初始化)。
  4. 集合框架:通过接口操作不同实现类(如 List list = new ArrayList();)。
  5. 事件监听:Swing 事件处理、Web 框架的事件回调机制。
五、多态相关的面试 / 笔试真题示例
  1. 代码分析题

    java

    class A { void f() { System.out.println("A"); } }
    class B extends A { void f() { System.out.println("B"); } }
    class C extends B { void f() { System.out.println("C"); } }
    public class Test {public static void main(String[] args) {A a1 = new B(); A a2 = new C();a1.f(); a2.f();}
    }
    
     

    输出:B 和 C(多态调用子类重写方法)。

代码解释

  1. 多态机制

    • 变量 a1 和 a2 被声明为 A 类型,但分别指向 B 和 C 的实例。
    • Java 中,方法调用基于对象的实际类型(而非引用类型),因此会调用实际类型中重写的 f() 方法。
  2. 方法重写

    • B 和 C 类均重写了父类 A 的 f() 方法。
    • a1.f() 调用的是 B 类的 f(),输出 B
    • a2.f() 调用的是 C 类的 f(),输出 C

Q:所以说实际创建的都是子类的实例,但为什么类型是父类?

A:(我的理解是编译通过与否是看父类是否存在该方法,实际运行看子类的这个方法里面按照子类的方法体运行),因为编译器只关心引用类型(如A a1)是否包含目标方法(如f())。

在 Java 中,这种 “父类引用指向子类对象” 的机制是多态(Polymorphism)的核心体现,主要有以下原因:

1. 多态的实现基础,“父类引用指向子类对象” 的机制

Java 通过动态绑定(Runtime Dynamic Binding)实现方法调用,即:

  • 编译时:检查引用类型(如A a1)是否存在目标方法(如f())。
  • 运行时:根据对象的实际类型(如new B())调用对应的重写方法。

示例

java

A a1 = new B();  // 引用类型为A,实际类型为B
a1.f();          // 编译时检查A类有f()方法,运行时调用B类的f()

2. 为什么需要父类引用?

(1)代码复用与扩展性

通过父类类型作为参数或返回值,可以统一处理不同子类的对象:

java

// 接收父类A的引用,可传入任何A的子类对象
void process(A obj) {obj.f();  // 调用实际子类的f()方法
}process(new B());  // 输出B
process(new C());  // 输出C
(2)接口与抽象类的应用

父类常被设计为抽象类接口,强制子类实现特定方法:

java

interface Shape {double area();  // 抽象方法
}class Circle implements Shape {@Overridepublic double area() { return Math.PI * r * r; }
}Shape s = new Circle();  // 接口引用指向实现类
s.area();                // 调用Circle的area()

3. 与静态类型语言的关系

Java 是静态类型语言,变量类型必须在编译时确定。通过父类引用,可以在保证类型安全的前提下,实现运行时的动态行为:

java

A a = new B();  // 合法:B是A的子类
B b = new A();  // 非法:A不是B的子类

总结

引用类型实际对象作用
父类(如 A)子类实例实现多态,提高代码灵活性与复用性
子类(如 B)子类实例访问子类特有方法(但失去多态优势)

关键原则:父类引用只能调用父类中定义的方法(即使实际对象重写了该方法),但具体执行逻辑由实际类型决定。

  1. 概念辨析题

    • “多态是否适用于成员变量?”
      答案:不适用。成员变量访问由编译期类型决定,与对象实际类型无关(见前文表格)。
  2. 简答题

    • “简述多态的作用及实现条件。”
      要点:解耦、可扩展性、代码复用;需满足继承 / 实现、方法重写、父类引用指向子类对象。
六、快速记忆口诀
  • 多态三要素:继承重写加父引,运行动态绑方法。
  • 成员访问规则方法看右边(运行时对象),变量看左边(编译时类型),静态方法也看左。
  • 应用场景:接口回调策略模,模板集合事件多。

掌握这些核心考点,应对考试中的多态题目基本无压力啦! 🚀

7 方法重写(方法覆盖)不可以降低访问权限

核心特点:

  1. 方法签名必须相同

    • 方法名、参数列表、返回类型(Java 5+ 支持协变返回类型,即子类返回类型可以是父类返回类型的子类)必须与父类完全一致。
  2. 访问权限不能更严格(子不能比父严格)

    • 子类方法的访问修饰符(如publicprotected)不能比父类更严格(例如父类是protected,子类不能是private)。
  3. 异常声明范围更小

    • 子类方法抛出的异常必须是父类方法声明异常的子类或不抛出异常。
  4. 只针对实例方法

    • 静态方法static)和私有方法private)不能被覆盖(但子类可以定义同名方法,称为隐藏)。

继承中的成员变量隐藏方法调用规则

关键点分析:

  1. 成员变量隐藏

    • Test类中的private String name与父类Father中的private String name是两个独立的变量。
    • 子类无法直接访问父类的私有变量,因此子类的name变量隐藏了父类的同名变量,但不会影响父类方法的实现。
  2. 方法调用规则

    • test.getName()调用的是父类FathergetName()方法。
    • 父类的getName()方法返回的是父类自身的name变量(值为"father"),而非子类的name变量。

执行流程:

java

Test test = new Test();       // 创建Test对象
System.out.println(test.getName());  // 调用父类的getName()方法// 返回父类的name变量 → "father"

输出结果:

plaintext

father

为什么不是 String 不可变性?

  • String的不可变性是指字符串对象本身不可修改,但可以通过引用指向新的字符串对象。
  • 在本题中,无论name是否为String类型,结果都一样,因为:
    • 父类方法返回的是父类的变量,与子类变量无关。
    • 即使子类修改了自己的name变量,也不会影响父类的同名变量。

验证代码:

java

public class Test extends Father {private String name = "test"; // 隐藏父类的name变量public static void main(String[] args) {Test test = new Test();System.out.println(test.getName()); // 输出 "father"// 子类可以修改自己的name变量,但不影响父类方法test.name = "modified";System.out.println(test.getName()); // 仍输出 "father"}
}class Father {private String name = "father";public String getName() {return name; // 返回父类的name变量}
}

总结:

  • 考点:继承中的变量隐藏和父类方法的作用域。
  • String 不可变性与此题无关,因为本题未涉及字符串修改操作,仅涉及变量访问和方法调用。

接口之间可以多继承(Multiple Inheritance),但是类是单继承的!

是的,在 Java 中,接口之间可以多继承(Multiple Inheritance),即一个接口可以同时继承多个父接口。这与类的单继承(一个类只能继承一个父类)形成对比。

接口多继承的语法:

java

interface InterfaceA {void methodA();
}interface InterfaceB {void methodB();
}// 接口C继承自InterfaceA和InterfaceB(多继承)
interface InterfaceC extends InterfaceA, InterfaceB {void methodC();
}// 实现类必须实现所有继承的方法
class MyClass implements InterfaceC {@Overridepublic void methodA() { System.out.println("Method A"); }@Overridepublic void methodB() { System.out.println("Method B"); }@Overridepublic void methodC() { System.out.println("Method C"); }
}

关键点:

  1. 接口支持多继承

    • 一个接口可以通过extends关键字同时继承多个父接口,用逗号分隔。
  2. 方法签名合并

    • 如果多个父接口中存在相同方法签名(方法名、参数列表、返回类型均相同),子接口不会报错,而是继承该方法。
    • 如果方法签名相同但返回类型或异常声明冲突,会导致编译错误。
  3. 默认方法冲突

    • 如果多个父接口提供了同名且参数相同的默认方法,子接口必须重写该默认方法以解决冲突:

      java

      interface A {default void foo() { System.out.println("A"); }
      }interface B {default void foo() { System.out.println("B"); }
      }interface C extends A, B {@Overridedefault void foo() {A.super.foo(); // 显式调用A的默认方法}
      }
      

为什么类不支持多继承,而接口支持?

  • 类的多继承问题(菱形继承):
    若类C同时继承自AB,而AB有同名方法,C将无法确定使用哪个实现,导致歧义。

  • 接口的多继承安全

    • 接口中的方法默认是抽象的(Java 8 前),不存在方法实现冲突。
    • Java 8 引入默认方法后,通过强制子接口重写冲突的默认方法,避免了歧义。

示例:多继承的应用

java

interface Flyable {void fly();
}interface Swimmable {void swim();
}// 多继承:同时具备飞行和游泳能力
interface AmphibiousVehicle extends Flyable, Swimmable {void switchMode();
}// 实现类需实现所有接口方法
class FlyingBoat implements AmphibiousVehicle {@Overridepublic void fly() { System.out.println("Flying..."); }@Overridepublic void swim() { System.out.println("Swimming..."); }@Overridepublic void switchMode() { System.out.println("Switching mode..."); }
}

总结:

特性类(Class)接口(Interface)
继承限制单继承(extends 一个类)多继承(extends 多个接口)
冲突处理不允许方法实现冲突默认方法冲突需显式解决
设计目的定义对象的状态和行为定义行为规范(契约)

10 java 中二维数组 

结合题目 int a[6][7] 中 a[3][4] 之前的元素个数 进行详细说明:

1. 二维数组的定义与结构

在 Java 中,二维数组是数组的数组,本质上是嵌套结构。

java

int[][] a = new int[6][7];  // 定义一个6行7列的二维数组
  • 行索引范围0 到 5(共 6 行)。
  • 列索引范围0 到 6(每行 7 列)。
  • 存储方式:按行优先存储,即先存储第 0 行的所有元素,再存储第 1 行,依此类推。

2. 数组元素的索引规则

  • 索引从 0 开始a[3][4] 表示第 4 行(索引 3)、第 5 列(索引 4)的元素。
  • 内存布局:二维数组在内存中按一维顺序排列,例如:

    java

    a[0][0], a[0][1], ..., a[0][6],  // 第0行(7个元素)
    a[1][0], a[1][1], ..., a[1][6],  // 第1行(7个元素)
    ...
    a[5][0], a[5][1], ..., a[5][6]   // 第5行(7个元素)
    

3. 计算指定元素前的元素个数

对于 a[3][4],计算其之前的元素个数:

  1. 前 3 行的元素总数
    3 行 × 7 列/行 = 21 个元素
  2. 当前行(第 3 行)中 a[3][4] 之前的元素
    第 3 行的索引范围是 0 到 3(共 4 个元素)。
  3. 总数21 + 4 = 25 个元素。

4. 通用计算公式

对于二维数组 a[m][n],元素 a[i][j] 之前的元素个数为:

java

元素个数 = i × n + j
  • 示例a[3][4] 的元素个数为 3 × 7 + 4 = 25

5. Java 代码验证

java

public class Main {public static void main(String[] args) {int[][] a = new int[6][7];int count = 0;int targetRow = 3;int targetCol = 4;// 遍历前3行的所有元素for (int i = 0; i < targetRow; i++) {for (int j = 0; j < 7; j++) {count++;}}// 遍历当前行(第3行)中a[3][4]之前的元素for (int j = 0; j < targetCol; j++) {count++;}System.out.println("a[3][4]之前的元素个数:" + count); // 输出25}
}

6. 常见易错点

  1. 索引混淆:行 / 列索引从 0 开始,a[3][4] 是第 4 行第 5 列的元素。
  2. 计算行数:前 3 行的索引是 012(共 3 行),而非 0 到 3(4 行)。
  3. 列元素计算:当前行中 a[i][j] 之前的列索引是 0 到 j-1,共 j 个元素。

总结

通过本题掌握:

  • 二维数组的行优先存储规则。
  • 索引与元素位置的映射关系。
  • 快速计算指定位置前的元素个数(i×n + j)。

11 java声明变量的方括号 [] 前不能直接填数组长度

在 Java 中,方括号 [] 前不能直接填数组长度,这是明确的语法规则。!!!!!

但有以下两种情况可能被混淆,需要区分:

1. 动态初始化数组时,在 new 关键字后指定长度

合法语法

java

int[] a = new int[5];  // 在 new 后指定长度 5,创建数组
  • 关键点
    • 长度必须写在 new 关键字后的方括号内,而非声明变量时的方括号内。
    • 声明变量时,方括号 [] 必须为空(无论放在类型后还是变量名后)。

错误示例

java

int[5] a;         // 错误:声明变量时不能在 [] 中写长度
int a[5] = ...;   // 错误:无论静态/动态初始化,变量后的 [] 都不能写长度

2. 静态初始化数组时,直接通过元素列表隐式指定长度

合法语法

java

int[] a = {1, 2, 3};  // 元素列表隐含长度为 3
  • 关键点
    • 静态初始化(使用 {} 语法)时,绝对不能在任何方括号内写长度。!!!!!!
    • 以下写法均错误:

      java

      int[3] a = {1, 2, 3};    // 错误
      int a[3] = {1, 2, 3};    // 错误
      int[] a = new int[3]{1,2,3};  // 错误:静态初始化不能用 new + 长度
      

总结:唯一合法的 “指定长度” 场景!!!!

只有在 动态初始化 时,才能在 new 关键字后使用 [长度]

java

int[] a;            // 声明变量([] 为空)
a = new int[5];     // 在 new 时指定长度

其他情况下,方括号 [] 前或内部都不能直接写长度。

12 java 集合与泛型考试知识点清单

1. 泛型(Generics)

概念
  • 作用:在编译时检查类型安全,避免类型转换异常(ClassCastException)。
  • 语法:使用<>声明泛型类型,如List<String>Map<Integer, String>
示例代码

java

// 不使用泛型(需手动类型转换,可能出错)
List list = new ArrayList();
list.add("hello");
String str = (String) list.get(0); // 需要强制类型转换// 使用泛型(编译时检查类型)
List<String> list = new ArrayList<>();
list.add("hello");
String str = list.get(0); // 无需类型转换,编译器保证类型安全

2. 集合框架(Collection Framework)

概念
  • 集合:存储对象的容器,提供统一操作接口。
  • 架构
    • 顶层接口Collection(单列集合)、Map(双列键值对)。
    • 子接口List(有序可重复)、Set(无序唯一)、Queue(队列)。
    • 实现类ArrayListLinkedListHashSetTreeSetHashMapTreeMap等。

3. 常用集合类型及方法

(1)List 接口
  • 特点:有序、可重复,支持索引访问。
  • 实现类ArrayList(动态数组)、LinkedList(双向链表)。

示例代码

java

List<String> list = new ArrayList<>();
list.add("apple");    // 添加元素
list.add("banana");
list.add(0, "cherry"); // 指定位置插入
System.out.println(list.get(1)); // 访问元素:banana
list.remove(0);        // 按索引删除
boolean exists = list.contains("apple"); // 判断元素是否存在
(2)Set 接口
  • 特点:无序、唯一(不允许重复元素)。
  • 实现类HashSet(哈希表)、TreeSet(红黑树,自动排序)。

示例代码

java

Set<Integer> set = new HashSet<>();
set.add(100);
set.add(200);
set.add(100); // 重复元素,添加失败
System.out.println(set.size()); // 输出:2
boolean has100 = set.contains(100); // 判断元素是否存在
(3)Map 接口
  • 特点:存储键值对(Key-Value),键唯一。
  • 实现类HashMap(哈希表)、TreeMap(红黑树,按键排序)。

示例代码

java

Map<String, Integer> map = new HashMap<>();
map.put("apple", 1);    // 添加键值对
map.put("banana", 2);
map.put("apple", 3);    // 键重复,覆盖原值
int value = map.get("apple"); // 获取值:3
map.remove("banana");  // 按键删除
boolean hasKey = map.containsKey("apple"); // 判断键是否存在

4. 迭代器(Iterator)

作用

遍历集合元素,支持remove()操作。

使用方法

java

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");// 使用 Iterator 遍历
Iterator<String> it = list.iterator();
while (it.hasNext()) {String element = it.next();if (element.equals("B")) {it.remove(); // 安全删除当前元素}
}
System.out.println(list); // 输出:[A, C]

5. 集合的选择场景

集合类型特点适用场景
ArrayList动态数组,随机访问快频繁查询、少插入删除
LinkedList双向链表,插入删除快频繁插入删除、少查询
HashSet无序唯一,基于哈希表去重、无需排序
TreeSet有序唯一,基于红黑树自动排序(如按字母、数值顺序)
HashMap键值对,无序,基于哈希表快速查找、插入删除
TreeMap键值对,有序,基于红黑树按键排序的键值对(如按时间戳)

6. 常见面试题

  1. 泛型的边界是什么?

    • 答:使用extendssuper限定泛型类型范围,如<T extends Number>(T 必须是 Number 或其子类)。
  2. ListSet的区别?

    • 答:List有序可重复,Set无序唯一。
  3. HashMapHashtable的区别?

    • 答:HashMap非线程安全、允许 null 键值,Hashtable线程安全、不允许 null。
  4. 如何遍历Map

    • 答:

      java

      // 方法1:遍历键集
      for (String key : map.keySet()) { ... }// 方法2:遍历键值对(推荐)
      for (Map.Entry<String, Integer> entry : map.entrySet()) {String key = entry.getKey();Integer value = entry.getValue();
      }
      

7. 实战技巧

  • 初始化集合

    java

    List<String> list = List.of("A", "B", "C"); // 不可变集合(Java 9+)
    Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
    
  • 集合转数组

    java

    List<String> list = List.of("A", "B", "C");
    String[] array = list.toArray(new String[0]);
    
  • 排序

    java

    List<Integer> list = Arrays.asList(3, 1, 2);
    Collections.sort(list); // 升序排序:[1, 2, 3]
    

通过以上知识点和代码示例,可系统掌握 Java 集合与泛型的核心内容。

相关文章:

  • VB逆向基础(一)
  • 【数据库】KingbaseES在线体验平台深度测试:从基础操作到增删改查实战
  • ffmpeg webm 透明通道视频转成rgba图片
  • 九日集训第六天
  • 基于Qt的配置管理界面实现:保存与加载配置文件
  • 338比特位技术
  • Day03_数据结构(手写)
  • rockylinuxapache和Linux服务配置
  • Python+pymysql中select count(*)/select *使用方式
  • 安装谷歌vue开发工具插件devtools支持vue2
  • linux内核-写时复制之实验+源码分析
  • Ajax 核心知识点全面总结
  • day40- 硬件学习之 51单片机II (中断处理)
  • 【MySQL】MySQL 数据库操作与设计
  • 网络编程TCP与UDP
  • 《开窍》读书笔记9
  • vulnhub-Matrix-Breakout 2 Morpheus
  • 网络NAT是什么
  • Go 语言中的条件判断和for 循环
  • 详解 MyBatis - Plus 服务层设计:让 CRUD 更高效、业务拓展更灵活——补充
  • 做那事的网站/百度网站
  • 网站loading动画/国内5大搜索引擎
  • 做rom的网站/千锋教育和达内哪个好
  • 张店学校网站建设公司/好的产品怎么推广语言
  • 做动图素材网站/网络营销方式方法
  • wordpress响应式concise主题/拼多多关键词优化步骤