自从有了AI,开始vibe coding,感觉越来越懒了,一些简单的代码都懒得写,能用AI就不动手。都快感觉之前的编程能力有退化迹象 各位佬,现在还回得去没有AI的编程日子吗 22 个帖子 - 18 位参与者 阅读完整话题
其他内容参见 JAVA学习记录总贴 本期是oop的简单练习,确实都很简单,主要负责熟手 第一题 public static void main(String[] args) { Car c = new Car(); Car car = new Car(100); System.out.println(c); System.out.println(car); } class Car { double price = 10; static String color = "white"; public String toString() { return price + "\t" + color; } public Car(){ this.price = 9; this.color = "black"; } public Car(double price){ this.price = price; } } 答案: 9.0 black 100.0 black 分析: 这道题应该挺好理解的。 注意 color 是一个静态变量,在第一次 new Car 的时候,static变量 就已经被修改了。 所以,最后两个实例的静态变量都是 black。 第二题 不难,纯粹练手 在Frock类中声明私有的静态属性currentNum[int类型],初始值为100000,作为衣服出厂的序列号起始值。 声明公有的静态方法getNextNum,作为生成上衣唯一序列号的方法。每调用一次,将currentNum增加100,并作为返回值。 在Homework02类的main方法中,分两次调用getNextNum方法,获取序列号并打印输出。 在Frock类中声明serialNumber(序列号)属性,并提供对应的get方法。 在Frock类的构造器中,通过调用getNextNum方法为Frock对象获取唯一序列号,赋给serialNumber属性。 在Homework02类的main方法中,分别创建三个Frock 对象,并打印三个对象的序列号,验证是否为按100递增。 答案: package hspedu.homework; public class Homework02 { public static void main(String[] args) { System.out.println(Frock.getNextNum()); System.out.println(Frock.getNextNum()); Frock f1 = new Frock(); System.out.println(f1.getSerialNumber()); Frock f2 = new Frock(); System.out.println(f2.getSerialNumber()); Frock f3 = new Frock(); System.out.println(f3.getSerialNumber()); } } class Frock { private static int currentNum = 100000; private int serialNumber; public static int getNextNum() { currentNum += 100; return currentNum; } public Frock() { serialNumber = getNextNum(); } public int getSerialNumber() { return serialNumber; } } 第三题 一个简单的多态练习,题干如下 动物类Animal包含了抽象方法 shout(); Cat类继承了Animal,并实现方法shout,打印“猫会喵喵叫” Dog类继承了Animal,并实现方法shout,打印“狗会汪汪叫” 在测试类中实例化对象Animal cat = new Cat(),并调用cat的shout方法 在测试类中实例化对象Animal dog = new Dog(),并调用dog的shout方法 答案: public class Homework03 { public static void main(String[] args) { Animal cat = new Cat(); cat.shout(); Animal dog = new Dog(); dog.shout(); } } abstract class Animal{ abstract void shout(); } class Cat extends Animal{ @Override void shout() { System.out.println("猫会喵喵叫"); } } class Dog extends Animal{ @Override void shout() { System.out.println("狗会汪汪叫"); } } 第四题 计算器接口具有 work 方法,功能是运算,有一个手机类 Cellphone,定义方法 testWork 测试计算功能,调用计算接口的 work 方法 要求调用 CellPhone 对象 的 testWork 方法,使用上匿名内部类 答案: ps:设计匿名内部类,建议还是认真写一下 public class Homework04 { public static void main(String[] args) { Cellphone cellphone = new Cellphone(); Calculator cal = new Calculator() { @Override public void work(int a, int b) { System.out.println(a + b); } }; cellphone.testWork(cal, 7, 8); } } interface Calculator { void work(int a, int b); } class Cellphone { public void testWork(Calculator calculator, int a, int b) { System.out.println("测试计算功能"); calculator.work(a, b); } } 第五题 编一个类 A,在类中定义局部内部类 B,B 中有一个私有常量 name,有一个方法 show () 打印常量 name。进行测试 进阶:A 中也定义一个私有的变量 name,在 show 方法中打印测试 答案:a piece of cake public class Homework05 { public static void main(String[] args) { new A().new B().show(); } } class A{ private final String name = "AAA"; class B{ private final String name = "BBB"; public void show(){ System.out.println(name); System.out.println(A.this.name); } } } 第六题 有一个交通工具接口类Vehicles,有work接口 有Horse类和Boat类分别实现Vehicles 创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat 有Person类,有name和Vehicles属性,在构造器中为两个属性赋值 实例化Person对象“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boat作为交通工具 额外:使用匿名内部类,增加一个用飞机过火焰山的方法,因为只用一次,因此不要写一个新的类 答案: public class Homework06 { public static void main(String[] args) { Person tang = new Person("唐僧", null); tang.passRiver(); tang.common(); tang.passRiver(); tang.passMountain(); } } interface Vehicles { void work(); } class Horse implements Vehicles { @Override public void work() { System.out.println("horse is working"); } } class Boat implements Vehicles { @Override public void work() { System.out.println("boat is working"); } } class Plane implements Vehicles { @Override public void work() { System.out.println("plane is working"); } } class Factory { private static final Horse HORSE = new Horse(); private static final Boat BOAT = new Boat(); private static final Plane PLANE = new Plane(); public static Horse getHorse() { return HORSE; } public static Boat getBoat() { return BOAT; } public static Plane getPlane() { return PLANE; } } class Person { private String name; private Vehicles vehicle; public Person(String name, Vehicles vehicle) { this.name = name; this.vehicle = vehicle; } public void setVehicle(Vehicles vehicle) { this.vehicle = vehicle; } public void passRiver() { if (!(vehicle instanceof Boat)) { vehicle = Factory.getBoat(); } vehicle.work(); } public void common() { if (!(vehicle instanceof Horse)) { vehicle = Factory.getHorse(); } vehicle.work(); } public void passMountain() { vehicle = new Vehicles(){ @Override public void work() { System.out.println("plane is working"); } }; vehicle.work(); } } 简要分析: 用懒汉式单例,保证只创建一个船和马对象,节省资源 用 vehicle instanceof Horse 来判断具体情况 第七题 有一个 Car 类,有属性 temperature(温度),车内有 Air(空调)类,有吹风的功能 flow,Air 会监视车内的温度,如果温度超过 40 度则吹冷气。如果温度低于 0 度则吹暖气,如果在这之间则关掉空调。实例化具有不同温度的 Car 对象,调用空调的 flow 方法,测试空调吹的风是否正确。 public class Homework07 { public static void main(String[] args) { Car car = new Car(42); Car.Air air = car.getAir(); air.flow(); Car car1 = new Car(2); Car.Air air1 = car1.getAir(); air1.flow(); Car car2 = new Car(-1); Car.Air air2 = car2.getAir(); air2.flow(); } } class Car{ private double temperature; public Car(double temperature) { this.temperature = temperature; } class Air{ public void flow(){ if(temperature > 40){ System.out.println("吹冷风"); } else if(temperature < 0){ System.out.println("吹暖风"); } else { System.out.println("关闭空调"); } } } public Air getAir(){ return new Air(); } } 第八题 创建一个Color枚举类 有 RED,BLUE,BLACK,YELLOW,GREEN这五个枚举值/对象; Color有三个属性redValue, greenValue, blueValue, 创建构造方法,参数包括这三个属性, 每个枚举值都要给这三个属性赋值,三个属性对应的值分别是 red: 255,0,0 blue:0,0,255 black:0,0,0 yellow:255,255,0 green:0,255,0 定义接口,里面有方法show,要求Color实现该接口 show方法中显示三属性的值 将枚举对象在switch语句中匹配使用 1 个帖子 - 1 位参与者 阅读完整话题
枚举和注解 枚举 基础知识 枚举是一组常量的集合。枚举属于一种特殊的类,里面只包含一组有限的特定的对象。 其实枚举类是可以通过传统写法自定义的,写法为: 构造器私有化 不提供set方法 在类内部预先初始化好静态的实例,并且对外暴露 代码略,直接学习如何创建真正的枚举。 使用enum关键字来代替class 直接写FALL(“秋天”,“凉爽”),效果上等价于 public static final Season FALL = new Season(“秋天”,“凉爽”); 如果有多个常量对象,使用逗号间隔即可 使用 enum 实现枚举,必须把定义的常量对象写在枚举类的最前面 使用无参构造器时,可以把括号也省略,直接写FALL,举例 FALL, SPRING, SUMMER, WINTER; public class Enum { public static void main(String[] args) { System.out.println(Season.SPRING); } } enum Season{ SPRING("春天","温暖"), SUMMER("夏天","炎热"), FALL("秋天","凉爽"), WINTER("冬天","寒冷"); private String name; private String desc; Season(String name, String desc) { this.name = name; this.desc = desc; } @Override public String toString() { return "Season{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } } .java文件可以用 Javac 编译成.class文件,.class文件也可以用 Javap 反编译成字节码文件,通过观察先编译再反编译的结果,可以看到很多隐藏的细节。 对于 Seanon 类,反编译得到的代码如下 Compiled from "Enum.java" final class hspedu.inner.enumer.Season extends java.lang.Enum<hspedu.inner.enumer.Season> { public static final hspedu.inner.enumer.Season SPRING; public static final hspedu.inner.enumer.Season SUMMER; public static final hspedu.inner.enumer.Season FALL; public static final hspedu.inner.enumer.Season WINTER; public static hspedu.inner.enumer.Season[] values(); public static hspedu.inner.enumer.Season valueOf(java.lang.String); public java.lang.String toString(); static {}; } 值得关注的细节有: 枚举类是 final 类型的,因此不可被继承 枚举类默认继承 java.lang.Enum 类,因此不可继承其他类 每一个常量都默认是 public static final 类型的 简单练习 第一题 判断语法正误 enum Gender { BOY, GIRL; } 答案: 语法没有错,相当于一个“没有属性,只含有无参构造器”的枚举类 第二题 判断输出什么 enum Gender2 { BOY, GIRL; } Gender2 boy = Gender2.BOY; Gender2 boy2 = Gender2.BOY; System.out.println(boy); System.out.println(boy2 == boy); 答案: BOY true 分析: 首先,枚举类本质也是类,所以Gender2 boy = Gender2.BOY;这种写法肯定是对的 boy相当于拿到了枚举类里的public static final常量,因此boy和boy2肯定是一样的 System.out.println(boy)相当于调用Gender2的toString方法,但是显然它没有,就去找父类的toString方法,父类是java.lang.Enum(前面有提到) Enum类的成员方法 分别是 name、ordinary、values、valueOf、compareTo方法,建议自己写一写用一用,方法的效果都写在代码里了: public class Enum { public static void main(String[] args) { Season spring = Season.SPRING; // name方法,建议优先使用toString,效果类似 System.out.println(spring.name()); System.out.println(spring); // ordinary方法,输出该枚举对象的序号,从0开始 System.out.println(spring.ordinal()); // values方法,返回所有的枚举对象 Season[] values = Season.values(); for (Season value : values) { System.out.println(value); } // valueOf方法,返回指定名称的枚举对象 Season valueOf = Season.valueOf("FALL"); System.out.println(valueOf); // compareTo方法,比较两个枚举对象,返回它们的序号之差,在这里是spring的序号 - valueOf的序号 System.out.println(spring.compareTo(valueOf)); } } enum Season{ SPRING("春天","温暖"), SUMMER("夏天","炎热"), FALL("秋天","凉爽"), WINTER("冬天","寒冷"); private String name; private String desc; Season(String name, String desc) { this.name = name; this.desc = desc; } } 简单练习2 声明 Week 枚举类,其中包含星期一至星期日的定义; MONDAY, TUESDAY, WEDNESDAY, THURSDAY,FRIDAY, SATURDAY, SUNDAY; 使用 values 返回所有的枚举数组,并遍历,要求打印值为“星期一”而不是“MONDAY” public class Enum { public static void main(String[] args) { Week[] values = Week.values(); for (Week value : values) { System.out.println(value); } } } enum Week{ MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日"); private String name; Week(String name) { this.name = name; } @Override public String toString() { return name; } Enum类的接口 Enum类本身已经有了继承关系,因此不能继承其他类 但作为一个类,它仍然可以实现接口 interface Playing { void play(); } enum Music implements Playing { HARD_ROCK, POP, CLASSIC, ROCK, JAZZ; @Override public void play() { } } 注解 最基本的修饰符 最基本的三个修饰符分别是: Override:用来限定某个方法必须重写父类的方法,只能用于方法 SuppressWarnings:抑制编译器的警告 Deprecated:用来表示某个程序元素(比如类或者方法)已经过时 Override 其实对于正确的方法重写来说,加不加这个修饰符都可以。 但如果加了的话,编译器会检查你是否有正确地重写这个方法。如果不正确的话会报错,产生编译错误。 Override的源码如下,从 @Target (ElementType.METHOD) 上可以看出,这个修饰符只能用在方法上。 @interface 修饰的类都是注解类。 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } 顺带一提, @Target 是修饰注解的注解,也称为元注解。 Deprecated 用 @Deprecated 修饰符修饰的元素,暗示其已经过时了,不推荐再继续使用,但其实可以使用。 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE}) public @interface Deprecated { String since() default ""; boolean forRemoval() default false; } 从源码可以看出,该修饰符可以修饰方法、字段、包、参数等。 该关键字一般用于 JDK 版本更迭时,给过时的方法打上标注。 SuppressWarnings 用来抑制编译器警告,在 ( ) 中可以填抑制的警告类型。 因为懒得打字,我直接把文档粘在这: 参数名称 作用描述 all 抑制所有警告。 unchecked 抑制与泛型相关的“未经检查的操作”警告,例如在使用原始类型时。 deprecation 抑制使用了 @Deprecated 标记的过时类或方法的警告。 rawtypes 抑制使用了泛型但未指定具体类型的“原始类型”警告。 unused 抑制代码中存在但未被使用的变量、方法或类的警告。 serial 抑制可序列化的类未定义 serialVersionUID 的警告。 null 抑制与空值分析相关的警告(如潜在的空指针)。 cast 抑制与强制类型转换操作相关的警告。 fallthrough 抑制在 switch 语句中缺少 break 而导致“直通”的警告。 finally 抑制 finally 块无法正常返回的警告。 boxing 抑制与自动装箱(boxing)和拆箱(unboxing)操作相关的警告。 static-access 抑制不正确的静态成员访问方式的警告。 dep-ann 抑制使用过时注解的警告。 incomplete-switch 抑制 switch 语句中未覆盖所有枚举常量的警告。 javadoc 抑制与 Javadoc 相关的警告。 synthetic-access 抑制内部类访问未优化的警告。 resource 抑制与资源(如 Closeable )使用相关的警告。 restriction 抑制使用了不建议或禁止引用的警告。 使用示例,代码如下: @SuppressWarnings({"all"}) enum Music implements Playing { HARD_ROCK, POP, CLASSIC, ROCK, JAZZ; @Override public void play() { } } @SuppressWarnings是没有对使用位置限制的,从源码中也可以看出,它没有 @Target 去限制,源码如下: @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); } 元注解 注解的注解,看源码时可能遇到,没那么重要,快速过一下。 四种元注解: Retention:指定注解的作用范围,三种值SOURCE,CLASS,RUNTIME Target:指定注解可以在哪些地方使用 Documented:指定该注解是否会在javadoc体现,即在生成文档的时候,可以看到该注解 Inherited:子类会继承父类注解 这部分我战略性跳过了,稍微不太好理解,也有点深入了。 1 个帖子 - 1 位参与者 阅读完整话题
坚持不了一点!!! 1 个帖子 - 1 位参与者 阅读完整话题
我的其他笔记可以查看 JAVA学习记录总贴 内部类 基础知识 内部类的类的第五大成员。 五大成员分别是:属性、方法、构造器、代码块、内部类。 定义:一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为“内部类”,在外面的类称为“外部类”,内部类最大的特点就是可以访问私有属性,并且可以体现类与类之间的包含关系。 语法 class Outer{//外部类 class Inter{//内部类 } } class OtherOuter{//外部其他类 } PS:内部类是OOP的重难点,底层源码有大量的内部类,必须要要下来这一块。 分类 定义在外部类局部位置(比如方法内) 局部内部类(有类名) 匿名内部类(无类名,重点!!!!!!!) 定义在外部类的成员位置上 成员内部类(没用static) 静态内部类(使用static) class OuterClass { //成员内部类 class MemberInnerClass {} //静态内部类 static class StaticInnerClass{} //外部方法 public void outerMethod() { //局部内部类 class LocalInnerClass {} //匿名内部类 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("匿名内部类"); } }; runnable.run(); } } 局部内部类 细节 局部内部类定义在外部类的局部位置,比如在方法中,并且有类名。(代码块中也行,但罕见) 可以直接访问外部类的所有成员,包含私有的 不能添加访问修饰符,因为局部内部类本质上就是一个局部变量,局部变量不能使用修饰符,同理它也不能,但它可以使用final,因为局部变量也可以用final,顺带一提abstract也可以。 局部内部类是可以被继承的。 作用域:仅仅作用在定义它的方法或代码块中 局部内部类想访问外部类的成员,直接访问即可 外部类想访问局部内部类的成员,可以在作用域内实例化局部内部类,但是注意,必须在定义它之后再new 外部其他类不可能访问局部内部类,这个挺好理解,因为局部内部类本质局部变量,不在它作用域内。 如果外部类和局部内部类变量重名,则会遵循就近原则,优先访问到局部内部类的变量,如果想要访问外部类的成员,则用如下语法 外部类类名.this.成员名 ,ps:顺带一提,如果你不嫌脱裤子放屁,其实 外部类类名.this.成员名 这种语法,可以在类的所有地方精确调用到本类成员 再顺带一提,其实所有内部类都可以在内部继续写内部类,这也是它复杂的原因 class OuterClass { private int n1 = 10; private void m2() {} public void m1() { class InnerClass { public void show() { // 可以直接访问外部类的所有成员,包括私有类型,包括方法 System.out.println("n1 = " + n1); m2(); } } class A extends InnerClass{ } InnerClass innerClass = new InnerClass(); } } 建议对着上面的代码,每一条细节都去自己实践一遍,看看违反了会报什么错。 匿名内部类 重点中的重点,这部分所有代码建议自己真的看完书去手敲一下,不要指望看一遍能懂,更不要单纯相信遇到的时候让AI来解释就好,没那么轻松。 特点 本质是类,底层会有独立的class字节码文件 属于内部类,定义在外部类/代码块,这类局部位置中 没有类名,其实底层是有的,但是程序员不关心,因而匿名 同时也是一个对象,类定义好的同时就已经被创建了 基本语法 new 父类/接口名(构造参数列表){ // 类体:重写父类/实现接口的抽象方法,也可以新增自定义成员 }; 基于接口的匿名内部类 class Outer04 { private int n1 = 10; public void method() { // 基于接口的匿名内部类 // 1. 需求:使用IA接口,并创建对象 // 2. 传统方法是写一个类,实现接口,创建对象,并调用方法 // 3. 但如果我们的需求是,这个类只用一次,那么定义出来就有些浪费了,所以我们可以使用匿名内部类来简化开发 // inner的编译类型是IA,而inner的运行类型是匿名内部类 /*其实这里的底层含义是 class Outer04$1 implements IA { @Override public void cry() { System.out.println("匿名内部类实现了cry方法"); } } */ // 4. jdk底层创建了匿名内部类Outer04$1,然后创建了实例,并且把地址返回给inner // 5. 匿名内部类只能使用一次,不能再次使用 IA inner = new IA(){ @Override public void cry() { System.out.println("匿名内部类实现了cry方法"); } }; inner.cry(); System.out.println(inner.getClass()); IA inner1 = new IA(){ @Override public void cry() { System.out.println("匿名内部类实现了cry方法"); } }; System.out.println(inner1.getClass()); } } interface IA { public void cry(); } 值得说的细节在注释里都已经写明了,我在此补充一点 Outer04$1 就是 JVM 自动为第一个匿名内部类分配的名字,数字表示该类在外部类中出现的顺序。 基于类的匿名内部类 class Outer04 { private int n1 = 10; public void method() { Father fa = new Father("张三"); Father fa1 = new Father("张三"){}; // 证明匿名类创建的不是Father类,而是匿名内部类 System.out.println(fa.getClass()); System.out.println(fa1.getClass()); Father fa2 = new Father("张三"){ @Override public void show() { System.out.println("匿名内部类重写show()"); } }; fa2.show(); Animal animal = new Animal() { @Override public void eat() { System.out.println("匿名内部类重写eat()"); } }; animal.eat(); } } class Father { private String name; public Father(String name) { this.name = name; } public void show() { System.out.println("show()"); } } abstract class Animal{ public abstract void eat(); } 简单总结一些要点 匿名内部类可以重写方法 父类的构造器如果有参数,则你也要提供对应参数(如果有多个构造器,那你就提供符合其中一个的就行) 基于抽象类(接口)的匿名内部类,必须实现其中所有抽象方法 一些细节 匿名内部类,既有定义一个类的特性,也有创建对象的特性,是一个凉面派 可以直接访问外部类的所有成员,包括外部私有成员 不能添加访问修饰符(直觉看上去也没办法提交orz),因为它本质是局部变量 作用域仅在定义它的方法or代码块中 外部其他类也不能访问匿名内部类,因为其本质局部变量 如果外部类和匿名内部类变量重名,则会遵循就近原则,优先访问到局部内部类的变量,如果想要访问外部类的成员,则用如下语法 外部类类名.this.成员名 匿名内部类练习 题目1:写一个基于接口的匿名内部类,并把它作为方法参数传入 public class AnonymousClass { public static void main(String[] args) { // 方式1:直接在参数位置编写匿名内部类 f1(new AA() { @Override public void show() { System.out.println("匿名类实现接口"); } }); // 方式2:先创建匿名内部类对象,再传入参数 AA aa = new AA() { @Override public void show() { System.out.println("匿名类实现接口"); } }; f1(aa); } public static void f1(AA aa){ aa.show(); } } interface AA{ void show(); } 题目2: 定义一个铃声接口 Bell,接口中包含 ring() 方法。 定义一个手机类 Cellphone,类中包含闹钟功能方法 alarmclock(Bell bell),方法的参数类型为 Bell。 测试手机类的闹钟功能:通过匿名内部类创建 Bell 接口的实现对象,作为参数传入 alarmclock 方法,调用 ring() 方法时打印:懒猪起床了。 再传入另一个匿名内部类对象,调用 ring() 方法时打印:小伙伴上课了。 public class AnonymousClass { public static void main(String[] args) { new Cellphone().alarmclock(new Bell() { @Override public void ring() { System.out.println("懒猪起床了"); } }); new Cellphone().alarmclock(new Bell() { @Override public void ring() { System.out.println("小伙伴上课了"); } }); } } interface Bell { void ring(); } class Cellphone { public void alarmclock(Bell bell){ bell.ring(); } } 重点:一定要写一写感受一下,这部分涉及:多态、继承、动态绑定、内部类,多个知识点混杂在一起,需要认真练习 成员内部类 成员内部类定义在外部类的成员位置,并且没有static修饰 可以访问外部类的所有成员 可以添加任意的修饰符,因为其地位相当于一个成员 成员内部类的作用域和外部类的其他成员一样,都是整个类体。 成员内部类可以调用外部类的所有成员,包括私有。 外部类可以访问成员内部类的所有成员,包括私有成员,不过,必须要创建实例才能访问。 外部其他类想要访问成员内部类,有两种方法,分别标注在代码里了 如果外部类和内部类成员同名,则内部类访问时采取就近原则,如果一定要访问外部类的同名成员,则采用 外部类.this.成员名 的方式 public class AnonymousClass { public static void main(String[] args) { // 外部其他类访问成员内部类的两种方式 // 方式一 通过外部类对象访问成员内部类对象,下面的两种写法本质上是等价的 Outer.Inner inner = new Outer().new Inner(); // 写法1 Outer outer = new Outer(); // 写法2 Outer.Inner inner1 = outer.new Inner(); // 方式二 在外部类中编写一个写法,返回成员内部类对象 Outer.Inner inner2 = outer.getInner(); } } class Outer{ private int n1 = 10; private String name = "张三"; public Inner getInner(){ return new Inner(); } // 成员内部类 // 成员内部类是定义在外部类的成员位置上 public class Inner{ private int n2 = 20; public void say(){ System.out.println("n1 = " + n1 + " name = " + name); } } public void show(){ Inner inner = new Inner(); // 即使是类内部的私有成员也可以被访问,因为本质上它也是类的一部分。 System.out.println(inner.n2); } } 静态内部类 静态内部类定义在外部类的成员位置,并且有 static 修饰符。 静态内部类可以直接访问外部类的所有静态成员(包括私有的),但是不能直接访问非静态成员;可以访问本类的所有成员,不管是静态还是非静态 解释: 静态内部类是外部类的一部分,因而可以访问外部类的所有静态成员。 对于外部类的非静态成员,可以在静态内部类中实例化,然后访问。 可以添加任意访问修饰符,因为它的地位就是一个成员 作用域和其他的成员一样,是整个类体内部。 静态内部类可以在不依赖外部类的前提下被实例化 外部其他类访问静态内部类、静态内部类访问外部类、外部类访问静态内部类的逻辑都写在了代码里,如下所示 外部类和静态内部类成员重名时,静态内部类如果想要访问,则默认就近原则,如果想要访问外部类的同名成员,则需要 外部类名.成员 P.S. 内部类可以是静态的,但是顶层类不能是静态的。 public class AnonymousClass { public static void main(String[] args) { // 展示外部其它类访问静态内部类的方式 // 方式1,直接new Outer.Inner inner = new Outer.Inner(); inner.sayHello(); // 方式2,编写一个方法,返回静态内部类的实例。 Outer.Inner inner1 = new Outer().getInnerInstance("java"); inner1.sayHello(); // 方式2的补充,可以在外部类中写静态方法,返回静态内部类的实例,没有本质区别 Outer.Inner inner2 = Outer.getInnerInstance_("HSP"); inner2.sayHello(); } } class Outer { private int n1 = 10; private static int n2 = 20; // 静态内部类 public static class Inner { private String name; private static int count = 0; private static int n2 = 30; // 静态内部类可以有构造器 Inner(String name) { this.name = name; } Inner() { } // 静态类里可以有普通方法 public void sayHello() { // 展示静态内部类访问外部类的方式。 // System.out.println(n1); 不能直接访问外部类非静态成员变量 System.out.println(new Outer().n1); // 但是可以通过创建外部类实例来访问外部类非静态变量 System.out.println(Outer.n2); // 可以直接访问外部类静态变量 System.out.println(n2); // 直接访问遵循就近原则 System.out.println("Hello, I'm " + this.name); // 也可以访问本类非静态变量 System.out.println("Total count: " + count); // 也可以访问本类静态变量 } } // 接下来展示外部类如何访问静态内部类 public void accessInner() { Inner inner = new Inner("HSP"); // 创建静态内部类对象 System.out.println(inner.name); // 访问静态内部类成员变量 System.out.println(Inner.count); // 访问静态内部类静态成员变量 } public Inner getInnerInstance(String name) { return new Inner(name); // 创建静态内部类对象并返回给调用方 } public static Inner getInnerInstance_(String name) { return new Inner(name); // 创建静态内部类对象并返回给调用方 } } 综合练习 练习1 当前代码会不会报错?为什么? public class Test { class Inner { public int a = 5; } public static void main(String[] args) { Inner r = new Inner(); } } 答案: 会报错,因为成员内部类在创建时依赖外部类的实例而存在,需要一个外部类作为容器,传统初始化方法为 “外部类实例.new 成员内部类名” main方法是静态的,静态方法中没有this关键字,因而报错,下面这样做就可以 public class Outer { public class Inner { } // 实例方法(非静态方法) public void show() { Inner inner = new Inner(); // ✅ 可以! // 等价于:this.new Inner(); // 因为实例方法中,this 指向当前 Outer 对象 } } 练习2 下面这段代码会输出什么? public class Test { public Test() { Inner s1 = new Inner(); s1.a = 10; Inner s2 = new Inner(); System.out.println(s2.a); } class Inner { public int a = 5; } public static void main(String[] args) { Test t = new Test(); Inner r = t.new Inner(); System.out.println(r.a); } } 答案: 5 5 分析: Test构造器里初始化的 S1 和 S2 分别是两个不同的成员内部类实例,而 main 方法里初始化的 r 也是一个不同的成员内部类实例。三个方法中的 a 是独立的 1 个帖子 - 1 位参与者 阅读完整话题
这是个悲伤的故事 5 个帖子 - 3 位参与者 阅读完整话题
一刷力扣top100,还是比较有成就感的.之前都是刷的洛谷,因为听说面试题会让撕top100,于是开始了面向面试学习. 1 个帖子 - 1 位参与者 阅读完整话题
开源一款高质量的高阶魔方求解器,古法+vibe 混合实现,整合了网络上的开源算法,可以快速解算出魔方 2~50 阶的求解步骤,(代码限制了 50 阶,可以自行解除限制,理论上支持无限阶),并且支持动画教学,支持拍照求解,并且支支持生成 pdf 逐步教程文档 由于 github pages 限制,演示图片不显示,建议下载代码本地部署、 演示: https://jingguanzhang.github.io/EasyCube/ 仓库: https://github.com/jingguanzhang/EasyCube
开源一款高质量的高阶魔方求解器,古法+vibe 混合实现,整合了网络上的开源算法,可以快速解算出魔方 2~50 阶的求解步骤,(代码限制了 50 阶,可以自行解除限制,理论上支持无限阶),并且支持动画教学,支持拍照求解,并且支支持生成 pdf 逐步教程文档 由于 github pages 限制,演示图片不显示,建议下载代码本地部署、 演示: https://jingguanzhang.github.io/EasyCube/ 仓库: https://github.com/jingguanzhang/EasyCube
开源一款高质量的高阶魔方求解器,古法+vibe 混合实现,整合了网络上的开源算法,可以快速解算出魔方 2~50 阶的求解步骤,(代码限制了 50 阶,可以自行解除限制,理论上支持无限阶),并且支持动画教学,支持拍照求解,并且支支持生成 pdf 逐步教程文档 由于 github pages 限制,演示图片不显示,建议下载代码本地部署、 演示: https://jingguanzhang.github.io/EasyCube/ 仓库: https://github.com/jingguanzhang/EasyCube
想问问佬友们 有碰到gpt接码的时候 会出现 你请求手机验证的次数过多。请稍后再试 这个情况吗 感觉之前 还没碰到过 今天发现这种情况了 该如何去解决 1 个帖子 - 1 位参与者 阅读完整话题
使用古法注册gpt的时候,手机号用的智利或者巴西的,接码没有问题,注册的时候提示这个地区不提供openAi服务这是怎么回事,各位佬你们用的是什么地区的手机号 11 个帖子 - 3 位参与者 阅读完整话题
(话题已被作者删除) 1 个帖子 - 1 位参与者 阅读完整话题
主人公:我大学同学 他所在的是一家外资企业,在gpt-4时期,公司就买了Gpt Team(现Business),直到今天他仍然采用古法编程。甚至对gpt team都没什么了解,估计也就偶尔使用web对话。 1 个帖子 - 1 位参与者 阅读完整话题
你请求手机验证的次数过多。请稍后再试。佬友们有知道这是什么原因的吗? [image] 7 个帖子 - 4 位参与者 阅读完整话题
相信有佬们已经知道了 现在PLUS的AT大法的路子已经断了 而PLUS的RT目前是基本上要绑手机号了 凌晨用CPA管理工具,查了一下号池 191个号,死了一半 查了一下邮件,有一部分是破限警告后被封号的 鉴于目前这个情况,我的小鸡毛公益站后续应该会加前置审核,禁止破限了 得保住我剩下的号池 一会儿功夫,又401了两个号,目前还有90多个健在的号,其中有一部分是at的,能查额度,但是基本上也算是死了,因为codex无法调用了 正在古法手搓PLUS 成本提升到了0.45美金3个号(因为要收codex的码,而codex不能像PayPal那样无限收,只能绑3个号) 希望有大佬研究出来不跳手机号的方法 我的公益站刚出道就来这么一个噩耗,难顶 10 个帖子 - 8 位参与者 阅读完整话题
手动接吗,还要等待,实在太累了,有没有可以节约人工操作的注册机 1 个帖子 - 1 位参与者 阅读完整话题
rt,切换账号OAuth登录,旧帐号就会401. 有什么办法可以解决免费号(已手机号验证)的导入吗 2 个帖子 - 2 位参与者 阅读完整话题
现在氛围编程比古法还费手 🤣 敲直板的手腕隐隐作痛,想试试左右分离的那种,国内键盘厂现在那么强不知道有哪家有做这种类型的不?
先使用手机号注册openai账号,然后codex验证时绑定手机号,最后一步出现这个错误 有没有大佬知道是缺少了哪一步呢 3 个帖子 - 3 位参与者 阅读完整话题