先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
无法访问子类 特有的成员(成员变量和成员方法)
如果想要访问,则需要判断和转型
第01节 基础理论
什么是内部类呢?
内部类就是说在一个类大括号里面,包含着另外一个类。
注意: 这里的包含关系,不是继承关系。(继承关系存在 is A 关系,子类可以看做父类)
生活实例:
身体和心脏的关系,就是一种包含关系。 在身体里面包含的有心脏。
内部类的分类有哪些?
(1)成员内部类。独立的分支:静态成员内部类
(2)局部内部类。独立的分支:匿名内部类
内部类的应用场景?什么时候会使用内部类
以前在学习 界面编程当中,有点击事件。(JavaScript 讲过点击的效果) 这里就会使用到内部类。
在 Android 编程当中使用较多。
有些 Java底层源代码当中,会使用到内部类。
第02节 成员内部类
位置
在一个类的成员变量或者成员方法的地方,编写类。(类当中,方法外)
代码
外部类和内部类
//身体(外部类)
public class Body {
String name = “身体”;
private int age = 18;
public void exercise(){
System.out.println(“身体成员方法…锻炼”);
}
//-------------------
class Heart{
String name = “心脏”;
public void jump(){
System.out.println(“心脏跳…蹦蹦蹦…”);
}
public void show(){
String name = “嘿嘿嘿”;
System.out.println(name); //嘿嘿嘿
System.out.println(this.name); //心脏
System.out.println(Body.this.name); //身体
System.out.println(Body.this.age); //18
}
}
//-------------------
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
//如果想要创建,内部类的对象,应该怎么使用呢?
//公式是: 外.内
Body.Heart bh = new Body().new Heart();
//调用成员变量和成员方法
System.out.println(bh.name); //心脏
bh.jump(); //心脏跳…蹦蹦蹦…
System.out.println(“--------”);
bh.show();
}
}
第03节 静态成员内部类
理论
-
位置: 类中方法外的类。保证他是一个成员内部类
-
添加 static 关键字。
-
效果: 可以直接使用类名称打点调用
代码
外部类和内部类
//外部类
@SuppressWarnings(“all”)
public class Outer {
//静态变量
static String name = “外部类”;
//内部类:静态内部类
static class Inner {
String name = “内部类”;
public void inNoStatic() {
System.out.println(“内部类…非静态方法”);
}
public static void inYesStatic() {
System.out.println(“内部类…静态方法”);
}
}
}
测试类
public class Test {
public static void main(String[] args) {
//创建对象。访问非静态方法
Outer.Inner one = new Outer.Inner();
//调用方法
one.inNoStatic(); //内部类…非静态方法
//如果想要调用静态内部类当中的静态方法。
Outer.Inner.inYesStatic(); //内部类…静态方法
}
}
第04节 局部内部类
理论
什么是局部内部类啊?
- 位置:
定义在方法当中的类,叫做局部内部类。
- 范围:
只能在方法里面使用,出了方法,无法使用。
代码
外部类和内部类
//外部类:演示局部内部类
@SuppressWarnings(“all”)
public class Outer {
String name = “外部类”;
//成员方法
public void show() {
//局部变量
int age = 18;
//打印输出
System.out.println(age);
}
//成员方法
public void method() {
//-------------------
class Inner {
String name = “内部类”;
public void say() {
System.out.println(“我是局部内部类成员方法”);
}
}
//-------------------
//创建对象
Inner iii = new Inner();
iii.say();
}
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
//只能创建外部类的对象
Outer ooo = new Outer();
//调用方法
ooo.method();
}
}
面试问题
第05节 匿名内部类
理论
- 介绍:
匿名内部类指的是没有名字的内部类,他是局部内部类的一种。
- 写法:
new 类名称/接口名称(参数){
//…方法…
};
由来
动物的接口
//动物的接口
@SuppressWarnings(“all”)
public interface Animal {
//吃的方法
public abstract void eat();
}
实现类
//实现类
@SuppressWarnings(“all”)
public class Dog implements Animal {
@Override
public void eat() {
System.out.println(“狗吃SHI”);
}
}
测试类
//测试类
public class Test {
//思考问题:我们能不能省略 实现类(Dog) 不写呢?
public static void main(String[] args) {
//版本1:原始的用法,创建实现类的对象,去调用方法
Dog one = new Dog();
one.eat(); //狗吃SHI
System.out.println(“---------”);
//版本2: 多态的写法,左父右子
Animal two = new Dog();
two.eat(); //狗吃SHI
System.out.println(“--------”);
//版本3: 匿名内部类的写法
Animal three = new Animal() {
@Override
public void eat() {
System.out.println(“狗吃SHISHI”);
}
};
three.eat();
}
}
应用场景
如果方法的参数写的是接口,传递的是接口的实现类。(匿名内部类他也是接口的实现类)
拓展点:反编译的指令 javap 类名称.class
接口代码
//定义接口
public interface Usb {
//定义抽象方法: 连接
public abstract void connect();
}
测试类
public class Test {
public static void main(String[] args) {
//直接调用方法
useUsb(new Usb() {
@Override
public void connect() {
System.out.println(“连接电脑”);
}
});
}
//定义一个使用USB接口的方法
public static void useUsb(Usb sb){
//调用方法
sb.connect();
}
}
//小结:
//如果我们方法的参数写的是接口或者是类
//那么传递参数的时候,写的是实现类或者是子类。(也可以是匿名内部类)
//本质上面来说,匿名内部类就是子类或者实现类。 也就是多态的用法
//反编译操作: javap 类名称.class
第01节 内存管理
内存划分图区域
五个区域介绍
1、程序计数器
- 较小
程序计数器是一块较小的内存空间,它的作用是当前线程执行指向字节码的行号指示器。
简单一点说,就是指挥程序执行那一条指令。(分支、循环、跳转、异常)
- 线程私有
Java虚拟机的执行,是由多条线程轮流切换处理器的时间来执行,
在任何一个时刻当中,一个处理器只会执行一条指令。
那么每个线程,都会存在一个独立的程序计数器,各个线程之间的程序计数器,不会受到影响的,独立存储。
- 无异常
如果线程正在执行的是一个 Java的方法,这个计数器记录的是正在执行的虚拟机字节码地址。
如果正在执行的是 Native 形式的方法,则计数器的值记录为空 Undefined
此时此刻内存区域是唯一一个在 Java规范当中没有任何的 OutOfMemoryError 情况区域。
2、虚拟机栈
- 线程私有
与程序计数器一样,Java虚拟机栈也是线程私有的,他的生命周期与线程相同。
- 描述Java方法执行的内存模型
每个方法被执行的时候,都会同时创建一个栈帧(Stack Frame 栈帧是方法运行时的基础数据结构)
用于存储局部变量表、操作栈、动态链接、方法出口灯信息。
每个方法被调用直到执行完毕的整个过程,都会对应一个栈帧在虚拟机栈,从入栈到出栈的过程。
- 异常
在Java虚拟机当中,规定了下面的两种异常情况。
【1】StackOverflowError JVM规定了栈的最大深度,如果执行方法超过最大深度,则出现栈溢出
【2】OutOfMemoryError JVM在扩展时候,无法申请到足够的内存,则出现内存溢出
3、本地方法栈
- 为 native 服务
虚拟机栈是为 Java服务的
本地方法栈是为 native 方法服务的(native方法底层是C\C++调用的是操作系统底层,例如声卡)
- 异常
与虚拟机栈一样,本地方法栈也会抛出 StackOverflowError 和 OutOfMemoryError
4、Java堆
- 最大
对于大多数应用来说,Java堆是 Java虚拟机管理的内存当中,最大的一块
- 线程共享
Java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候创建
- 存放实例
堆区域,唯一的目的就是存放对象的实例,几乎所有的对象实例,都是在这里分配。
另外说明:在Java虚拟机规范当中描述,所有的对象和数组都是在堆上面分配,
但是随着JIT编译器发展,在堆上分配的方式,渐渐地已经不再是那么的绝对了。
- GC
Java堆是垃圾收集器管理的主要区域。
如果从内存的回收的角度来看,现在收集器采用的是分代收集算法。在 Java的堆当中,细分为 新生代和老年代。
如果从内存分配的角度来看,线程共享的Java堆可以划分多个线程私有分配缓冲区。
不过,无论如何划分,都与存放内容无关,无论哪个区域,存储的都依然是对象实例。
进一步的划分,目的只是为了更好的回收内存,或者更快的分配内存。
5、方法区
方法区,存储的是已经被虚拟机加载的数据,他有以下三个特点:
-
线程共享。 方法区和Java堆一样,也是被各个线程共享的内存区域
-
存储数据类型。 类信息、常量、静态变量、即时编译器编译后的代码
-
异常。 如果系统定义太多的类,导致方法区溢出,虚拟机同样会出现内存溢出溢出 OutOfMemeryError
方法区,又可以划分成为两个区域:
- 运行时常量池。
存储的是编译期间生产的各类字面量和符号引用,这部分内容加载后会进入方法运行时常量池存放。
运行时常量池,受到方法区内存限制,当常量池无法申请到内存空间的时候,则出现 OutOfMemeryError
- 直接内存, 存在以下四个特点
A. 在虚拟机外的数据 (说明:直接内存,不是虚拟机当中的内容,而是虚拟机之外的内存)
B. 直接分配 (说明:在JDK1.4之后的NIO,引入通道 Channel 缓冲区Buffer,可以操作直接内存)
C. 受到设备内存大小限制 (说明:受到设备总大小限制,而不是Java堆大小限制)
D. 异常(说明:如果超过内存容量,也会出现 OutOfMemeryError)
第02节 垃圾回收
基础理论
对于 Java 而言,好处: 系统帮我们去控制垃圾回收的。(系统自动完成的,不由程序员控制)
对于 C++ 而言,不是这样的,他的垃圾需要手动清理。(程序员自己去控制)
一个对象生命周期
-
刚刚出世的阶段
-
青年期
-
老年期
配图
标记清除算法
说明
生活实例:
相当于先把货架上面的货物,标记记录下来,(有人买的、没人买的、空着的商品和位置记录)
然后再把没人买的商品统一的进行下架处理,这是垃圾收集器的 早期策略
工作原理:
-
标记所有需要回收的对象
-
标记完毕后,统一回收所有标记的对象
缺点:
- 效率低:
标记和清除的效率都不高
- 内存碎片:
标记清除后,会产生大量的不连续的内存碎片。
导致程序需要分配较大对象时,无法找到足够连续空间,不得不提前触发GC垃圾收集器
复制算法
说明
目的:
为了解决效率的问题,复制(copying)收集算法就出现了
工作原理:
-
将内存按照容量大小,划分成为大小相同的两个区域。(例如A区域和B区域)
-
每次使用A区域的时候,B区域空闲。
-
当A区域使用完毕之后,将存活的对象,复制到B区域当中,一次性清理掉A区域的内容。
-
这样的操作,每次都是回收半块内存,内存分配过程当中,无需考虑内存碎片的问题。
优点:
-
每次针对于半个内存区域进行内存的回收。
-
分配内存的过程当中不需要考虑内存碎片的复杂情况,只需要移动堆顶指针,按照顺序分配内存即可。
缺点:
-
浪费空间。把内存缩小一半用,太浪费空间。
-
有时候效率低。在对象存活率高的时候,需要进行较多的复杂操作,这时候,效率就低了。
标记整理算法
说明
目的:
在复制算法当中,如果不想浪费 50% 的空间,就需要有额外的空间进行分配担保。
以应对被使用内存当中,所有对象都存活的低端情况,所以养老区不能用这种算法。
工作原理:
-
标记所有需要存活的对象
-
标记完毕后,统一让存活的对象向一端移动
-
直接清理掉边界外的内存
分代收集算法
4、分代收集算法
说明
目的:
现代的商业虚拟机,垃圾回收都是采用分代收集算法。
这种算法会根据对象存活周期的不同,将内存划分成为几块,根据各个区域的特点采用最合适的收集算法。
分代收集内存划分:
- 新生代 Young Generation Space ----> YGC
别称:也叫做新生区
特点:每次垃圾收集都会有大批量的对象死去,只有少量的存活。
算法:采用的是复制算法。
- 老年代 Tenure Generation Space
别称:也叫做养老区
特点:因为对象的存活率高,没有额外的空间进行担保。
写在最后
很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。
最后祝愿各位身体健康,顺利拿到心仪的offer!
由于文章的篇幅有限,所以这次的蚂蚁金服和京东面试题答案整理在了PDF文档里
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
FFFFFF,t_70#pic_center)
说明
目的:
在复制算法当中,如果不想浪费 50% 的空间,就需要有额外的空间进行分配担保。
以应对被使用内存当中,所有对象都存活的低端情况,所以养老区不能用这种算法。
工作原理:
-
标记所有需要存活的对象
-
标记完毕后,统一让存活的对象向一端移动
-
直接清理掉边界外的内存
分代收集算法
4、分代收集算法
说明
目的:
现代的商业虚拟机,垃圾回收都是采用分代收集算法。
这种算法会根据对象存活周期的不同,将内存划分成为几块,根据各个区域的特点采用最合适的收集算法。
分代收集内存划分:
- 新生代 Young Generation Space ----> YGC
别称:也叫做新生区
特点:每次垃圾收集都会有大批量的对象死去,只有少量的存活。
算法:采用的是复制算法。
- 老年代 Tenure Generation Space
别称:也叫做养老区
特点:因为对象的存活率高,没有额外的空间进行担保。
写在最后
很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。
最后祝愿各位身体健康,顺利拿到心仪的offer!
由于文章的篇幅有限,所以这次的蚂蚁金服和京东面试题答案整理在了PDF文档里
[外链图片转存中…(img-GOB6uG7n-1713057531823)]
[外链图片转存中…(img-rkQeMOLk-1713057531824)]
[外链图片转存中…(img-fepeKKHI-1713057531824)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-VJnNheCG-1713057531824)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!