前言
我是计算机专业学生,写这篇文章的主要目的是记录我学习Java的心得,文章不会就Java的基础知识点分析介绍(书上都有),在此写的都是我认为重要核心的内容与我的学习理解,也希望大家能在观看后在评论区提出自己的看法。
Java面向对象常见知识点
基本类型
Java中的基本类型(也称为原始类型)是语言内置的数据类型,它们不是对象,而是直接存储在栈内存中。Java的基本类型包括:
整数类型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
浮点类型:float(4字节)、double(8字节)
字符类型:char(2字节)
布尔类型:boolean(1字节)
//低------------------------------------------------------>高
byte,short,char > int > long > float > double
这些类型的特点是占用内存小、访问速度快,但功能相对有限。基本类型由低容量变高容量时是自动类型转换,由高容量变低容量时需要强制类型转换。其中不能对boolean类型进行类型转换。
引用类型
引用类型是指那些通过引用(即内存地址)来访问的对象类型。引用类型包括:
类类型:类类型就是Java中的“类”,它就像一个模板,用来创建具体的对象。例如String、ArrayList、自定义类等。
接口类型:接口类型是一种特殊的类型,它定义了一组方法(行为)的规范,但不实现这些方法。例如Runnable、Comparable等。
数组类型:数组类型是Java中用于存储一组相同类型数据的引用类型。数组的特点是固定长度、相同类型、通过索引访问。数组本身是引用类型,数组变量存储的是数组对象在堆内存中的地址。例如int[]、String[]等。
引用类型的变量存储的是对象的引用(内存地址)【这里有点像C语言中的指针】,而不是对象本身。对象存储在堆内存中,而引用变量本身存储在栈内存中。例如,String str = "Hello"; 中,str是一个引用变量,指向堆内存中的String对象。
引用类型的对象通常需要通过new关键字来创建(除了字符串字面量和数组字面量)。引用类型的变量如果没有初始化,默认值是null,表示它不指向任何对象。
final关键字修饰的常量
final关键字用于修饰变量、方法或类,表示它们是不可变的。
final变量:一旦被赋值,就不能再修改。
/**基本类型**/
final int MAX_VALUE = 100;
MAX_VALUE=200;//编译错误,MAX_VALUE是一个常量,不能被重新赋值
/**引用类型**/
final Person person = new Person("Alice", 25);
person = new Person("Bob", 30); // 编译错误,不能修改final变量的引用
final方法:不能被重写(override)当你希望某个方法的行为在子类中保持不变时,可以使用final修饰方法。
class Parent {
final void display() {
System.out.println("This is a final method.");
}
}
class Child extends Parent {
void display() { // 编译错误,不能重写final方法
System.out.println("Trying to override final method.");
}
}
final类:不能被继承。当你希望某个类不能被扩展或修改时,可以使用final修饰类。
final class MyClass {
// 类的内容
}
class SubClass extends MyClass { // 编译错误,不能继承final类
}
final关键字常用于定义常量,确保程序中的某些值在运行时不会被修改。
static关键字修饰的静态量
在Java中,static关键字用于修饰类的成员(变量、方法、代码块等),表示这些成员属于类本身,而不是类的实例。static成员在类加载时初始化,并且在整个程序运行期间只有一份。
1. static修饰变量(静态变量)
静态变量属于类,而不是类的实例。所有实例共享同一个静态变量。
特点:
类级别:静态变量属于类,而不是对象。
共享性:所有实例共享同一个静态变量。
生命周期:静态变量在类加载时初始化,在程序结束时销毁。
class Counter {
static int count = 0; // 静态变量
Counter() {
count++; // 每次创建对象时,count自增
}
}
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.count); // 输出:2
}
}
2. static修饰方法(静态方法)
静态方法属于类,而不是类的实例。静态方法可以直接通过类名调用,而不需要创建对象。
特点:
类级别:静态方法属于类,而不是对象。
直接调用:可以通过类名直接调用,例如ClassName.methodName()。
限制:静态方法中不能直接访问非静态成员(变量或方法),因为非静态成员属于对象。
class MathUtils {
static int add(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
int result = MathUtils.add(1, 2); // 直接通过类名调用
System.out.println(result); // 输出:3
}
}
3. static代码块(静态代码块)
静态代码块在类加载时执行,用于初始化静态变量或执行一些静态操作。
特点:
类加载时执行:静态代码块在类加载时执行,且只执行一次。
初始化作用:通常用于初始化静态变量或执行一些复杂的静态操作。
class MyClass {
static int x;
static {
x = 10; // 静态代码块初始化x
System.out.println("Static block executed");
}
}
public class Main {
public static void main(String[] args) {
System.out.println(MyClass.x); // 输出:10
}
}
static的使用注意事项
1、不能与this或super一起使用:static成员属于类,而不是对象,因此不能使用this或super。
2、不能直接访问非静态成员:静态方法中不能直接访问非静态变量或方法,因为非静态成员属于对象。
慎用static:
static成员的生命周期较长,滥用static可能导致内存泄漏或线程安全问题。
abstract关键字修饰的抽象类
抽象类(可以说是比父类抽象程度更高的父类,允许不写方法体)是不能被实例化(不能被new的)的类,通常用于定义一些通用的行为和属性,并要求子类提供具体的实现。
抽象类可以包含抽象方法和非抽象方法。抽象类不能直接创建对象,只能通过子类实例化,且子类必须对父类的所有抽象方法进行重写。有抽象方法的类必须是抽象类,抽象类中不一定要有抽象方法,可以写具体方法。
abstract class Shape {
abstract void draw(); // 抽象方法
void display() { // 非抽象方法
System.out.println("Displaying shape");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing Circle");
}
}
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("Drawing Rectangle");
}
}
public class Main {
public static void main(String[] args) {
Shape myCircle = new Circle();
Shape myRectangle = new Rectangle();
myCircle.draw(); // 输出: Drawing Circle
myRectangle.draw(); // 输出: Drawing Rectangle
myCircle.display(); // 输出: Displaying shape
}
}
| 特性 | 抽象类 | 普通类 |
|---|---|---|
| 实例化 | 不能实例化 | 可以实例化 |
| 方法实现 | 可以包含抽象方法和非抽象方法 | 只能包含非抽象方法 |
| 构造方法 | 可以有构造方法 | 可以有构造方法 |
| 子类实现 | 子类必须实现抽象方法 | 子类可以选择是否重写父类的方法 |
| 访问修饰符 | 方法可以是 public、protected 等 | 方法可以是 public、protected 等 |
interface和implements修饰的接口
接口是一种完全抽象的类,用于定义一组方法签名,但不提供方法的实现。接口可以被类实现,一个类可以实现多个接口。
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | 不能实例化 | 不能实例化 |
| 方法实现 | 可以包含抽象方法和非抽象方法 | 只能包含抽象方法(默认是public abstract)(Java 8 后可以有默认方法) |
| 变量 | 可以包含普通变量和常量 | 只能包含常量(默认类型是public static final) |
| 继承 | 单继承 | 多继承(一个类可以实现多个接口) |
| 构造方法 | 可以有构造方法 | 不能有构造方法 |
| 访问修饰符 | 方法可以是 public、protected 等 | 方法默认是 public |
1. 定义接口
interface MyInterface {
// 抽象方法(默认是 public abstract)
void method1();
// 默认方法(Java 8 引入)
default void method2() {
System.out.println("Default method in interface");
}
/*
*默认方法允许接口提供方法的默认实现,而实现类可以选择是否重写该方法。
*默认方法的引入是为了在不破坏现有实现的情况下扩展接口的功能。
*/
// 静态方法(Java 8 引入)
static void method3() {
System.out.println("Static method in interface");
}
/*静态方法可以直接通过接口名调用,不需要通过实现类的对象。
*静态方法通常用于提供工具方法。
*/
// 常量(默认是 public static final)
int MY_CONSTANT = 100;
}
2. 实现接口
class MyClass implements MyInterface {
@Override
public void method1() {
System.out.println("Method1 implementation in MyClass");
}
}
3. 使用接口
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.method1(); // 输出: Method1 implementation in MyClass
obj.method2(); // 输出: Default method in interface
MyInterface.method3(); // 输出: Static method in interface
System.out.println(MyInterface.MY_CONSTANT); // 输出: 100
}
}
接口的多继承机制
接口的多继承是指一个接口可以同时继承多个接口,从而继承这些接口中定义的所有方法。子接口会继承父接口的所有抽象方法、默认方法和常量。
//1. 定义父接口
interface InterfaceA {
void methodA();
}
interface InterfaceB {
void methodB();
}
//2.定义子接口(多继承)
interface InterfaceC extends InterfaceA, InterfaceB {
void methodC();
}
//3. 实现子接口
class MyClass implements InterfaceC {
@Override
public void methodA() {
System.out.println("Method A");
}
@Override
public void methodB() {
System.out.println("Method B");
}
@Override
public void methodC() {
System.out.println("Method C");
}
}
//4.使用多继承的接口
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.methodA(); // 输出: Method A
obj.methodB(); // 输出: Method B
obj.methodC(); // 输出: Method C
}
}
栈区、堆区和方法区
该部分内容若想通过图示理解,可以点击以下链接去观看另一个up主:
1. 栈区(Stack)
栈区是线程私有的内存区域,每个线程都有自己的栈。栈区用于存储局部变量、方法调用和部分对象引用。
栈区的职能:
存储局部变量:
包括基本类型(如int、char等)和对象引用(如String、ArrayList等)。
局部变量的生命周期仅限于方法或代码块的执行期间。
void example() {
int x = 10; // x是局部变量,存储在栈区
String str = "Hello"; // str是局部引用变量,存储在栈区
}
存储方法调用信息:
每次调用方法时,会在栈中创建一个栈帧(Stack Frame),用于存储方法的参数、局部变量和返回地址。方法执行结束后,栈帧会被销毁。
线程私有:
每个线程都有自己的栈,栈区的大小是固定的(64位系统中默认线程栈大小为1MB),可以通过-Xss参数设置。
快速访问:
栈区的内存分配和释放速度很快,因为它是“后进先出”(LIFO)的结构。
2. 堆区(Heap)
堆区是Java虚拟机(JVM)中最大的一块内存区域,所有线程共享堆区。堆区用于存储对象实例和数组。
堆区的职能:
存储对象实例:
所有的对象实例(包括new关键字创建的对象)都存储在堆区。
Person person = new Person("Alice", 25); // person对象存储在堆区
存储数组:
数组(无论是基本类型数组还是引用类型数组)都存储在堆区。
int[] numbers = new int[10]; // numbers数组存储在堆区
垃圾回收:
堆区是垃圾回收(GC)的主要区域。JVM的垃圾回收器会定期清理不再使用的对象,释放内存。
动态分配:
堆区的大小可以动态调整,可以通过-Xmx和-Xms参数设置最大堆内存和初始堆内存。
线程共享:
堆区是所有线程共享的,因此需要考虑线程安全问题。
3. 方法区(Method Area)
方法区是JVM中的一块共享内存区域,用于存储类的元数据、静态变量和常量池。
方法区的职能:
存储类的元数据:
就是.class为后缀的所有内容,包括类的名称、字段、方法、构造方法等信息。
class Person {
String name;
int age;
}//Person类的元数据存储在方法区
存储静态变量:
静态变量(static修饰的变量)存储在方法区中。
class MyClass {
static int count = 0; // count是静态变量,存储在方法区中
}
存储常量池:
常量池存储了类中的常量(如字符串字面量、final常量等)。
String str = "Hello"; // "Hello"存储在常量池中
线程共享:
方法区是所有线程共享的。
永久代与元空间:
在JDK 8之前,方法区的实现称为永久代(PermGen)。
在JDK 8及之后,方法区的实现改为元空间(Metaspace),元空间使用本地内存(Native Memory),不再受JVM内存参数的限制。
| 内存区域 | 存储内容 | 特点 |
| 栈区 | 局部变量、方法调用、对象引用 | 线程私有、快速访问、后进先出 |
| 堆区 | 对象实例、数组 | 线程共享、动态分配、垃圾回收 |
| 方法区 | 类的元数据、静态变量、常量池 | 线程共享、存储类信息、JDK 8后改为元空间 |
Java面向对象的三大特性
封装
封装是将数据(属性)和操作数据的方法(行为)绑定在一起,并隐藏内部实现细节。通过private 访问修饰符限制对属性的直接访问,并提供 public 的 getter 和 setter 方法来访问和修改属性。在IDEA中能通过右键鼠标自动生成属性的getter和setter方法。
class Person {
private String name; // 私有属性
public String getName() { // getter
return name;
}
public void setName(String name) { // setter
this.name = name;
}
}
Java中的权限修饰符等级:
| 权限修饰符 | 同一个类 | 同一个包 | 不同包的子类 | 不同包的非子类 |
| public(1) | √ | √ | √ | √ |
| protected(2) | √ | √ | √ | × |
| default(3) | √ | √ | × | × |
| private(4) | √ | × | × | × |
1、public(公开型):权限最高的一种,可以被不同包、类访问
2、protected(受保护型):可以被同一包内的类调用,也可以被不同包的子类访问
3、default(默认型):只允许在同一个包中进行访问
4、private(私有型):只能在同一个类中被调用
继承
继承允许一个类(子类)从另一个类(父类)继承属性和方法,实现代码重用。使用 extends 关键字实现继承。Java只允许单继承,即一个子类只能继承一个父类,不能继承多个父类,但可以多层继承,类似于生物意义上的继承。
class Animal {
void eat() {
System.out.println("Animal is eating.");
}
}
class Dog extends Animal { // Dog 继承 Animal
void bark() {
System.out.println("Dog is barking.");
}
}
class erHa extends Dog{//erHa 继承 Dog
void chaijia(){
System.out.println("erHa is chaijia.");
}
}
erHa myDog = new erHa();
myDog.eat(); // 继承的父代的父代的方法
myDog.bark();//继承的父代的方法
myDog.chaijia();
同时注意Java的继承只能继承父类非私有的成员属性和成员方法,在访问变量(变量的标签在子类与父类都有且相同)时,默认遵循“就近原则”,即从子类局部到子类成员再到父类成员,此时若想访问子类成员,可用this关键字,若想访问父类成员,可用super关键字。
在构造方法(主要作用是对引用的对象中的成员属性赋初始值)上,子类中的所有构造方法(无参或有参)都会默认地访问到父类的无参构造方法(访问不到会报错),若父类中只有有参构造方法,那么子类在写构造方法时要在第一句用super(n)传参调用父类的有参构造方法。
在方法重写(子类和父类出现方法声明【包括方法名、参数列表和返回值类型】相同)时,要有“@Override”注解给编译器作提示,否则可能达不到重写的目的,同时子类重写时方法的访问权限要大于父类,父类的方法若为私有型,该方法不能被子类继承,更不能被重写。
同时在使用继承功能时不要一味的为图方便去获取其他类中的某个方法而去继承,而是要考虑该类是否能作为被继承类的子类。
多态
多态是指同一个方法在不同对象中有不同的实现方式。通过“方法重写(Override)”和“方法重载(Overload)”实现。向上转型是自动的,向下转型是强制的(父类引用转为子类时,要求转为子类的引用类型与new出来的对象的类型相同)。多态的访问特点:对于成员变量,编译看左边,执行看左边;对于成员属性,编译看左边,执行看右边(这里的左右是基于new对象时的等号的)。多态的好处是提高了程序的扩展性,弊端是不能直接使用子类的方法。
class Animal {
void sound() {
System.out.println("Animal makes a sound.");
}
}
class Dog extends Animal {
@Override
void sound() { // 方法重写
System.out.println("Dog barks.");
}
}
Animal myAnimal = new Dog(); // 向上转型
Dog newDog=(Dog)myAnimal; //向下转型
myAnimal.sound(); // 输出 "Dog barks."
984

被折叠的 条评论
为什么被折叠?



