包
包的作用
1,区分相同名字的类
2,当类很多时,可以更好管理类
3,控制访问范围
包的用法
package com.ljx
package 是打包
com.ljx 是包名
包的本质
创建不同的文件夹/目录来保存类文件
包的命名
规则:只能包含数字,字母,下划线,小圆点,不能数字开头,不能是关键词或保留字
规范:com.公司名.项目名.业务模块名
包的导入
import java.util.Arrays;
import java.util.Scanner;//导入util 包下的Scanner 类
//import java.util.*;//导入Java.until 下全部
public class Main {
public static void main(String[] args) {
int arr[] = {-2,33,1,4,32,543,22};
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i] + "\t");
}
}
}
注意事项
1,package 的作用是声明当前类所在的包,需要放在最上面
2,import放在package之后,在类定义的前面,可以有多句且无顺序
访问修饰符
1,public,公共级别,对外开放
2,protected,受保护级别,对子类和同一个包公开
3,默认,没有修饰符,向同一个包公开
4,private,只对本类公开
封装
在Java中,封装是一种面向对象编程的核心概念之一。它是指将数据和对数据的操作封装在一个单元中,以便控制对数据的访问。
封装通过将数据的访问限制在类的内部,同时提供公共方法来访问和修改数据,来实现对数据的保护和控制。这种方式可以隐藏数据的具体实现细节,使得类的使用者只需要关注如何使用公共方法来操作数据,而无需关心数据的内部表示和处理逻辑。
在Java中,封装可以通过以下方式实现:
-
使用访问修饰符(例如
private
、public
、protected
)来限制对类的成员的访问权限。通过将数据成员声明为private
,可以确保只有类的内部可以直接访问这些成员,其他类无法直接修改或访问这些数据。 -
提供公共的访问方法(也称为getter和setter方法)来访问和修改数据成员。通过公共方法,类的使用者可以通过调用这些方法来获取或修改数据,而不需要了解底层实现。
public class Person { private String name; private int age; public String getName() { return name; } public void setName(String newName) { name = newName; } public int getAge() { return age; } public void setAge(int newAge) { if (newAge >= 0) { age = newAge; } } }
继承(建立查找关系)
在Java中,继承是一种面向对象编程的概念,用于描述类与类之间的关系。继承允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。
通过继承,子类可以继承父类的非私有成员(包括属性和方法),并可以添加自己的特定属性和方法。这意味着子类可以重用父类的代码,并且可以在不修改父类的情况下进行扩展。
在Java中,使用关键字extends
来实现继承关系。子类使用extends
关键字后跟父类的名称来声明继承关系。子类可以访问父类的公共(public
)和受保护(protected
)成员,但不能访问私有(private
)成员。
// 父类 Animal
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + "正在吃...");
}
}
// 子类 Dog 继承自 Animal
class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void bark() {
System.out.println(name + "正在汪汪叫!");
}
}
// 子类 Cat 继承自 Animal
class Cat extends Animal {
public Cat(String name) {
super(name);
}
public void meow() {
System.out.println(name + "正在喵喵叫!");
}
}
// 测试代码
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("旺财");
dog.eat(); // 调用父类的方法
dog.bark(); // 调用子类自己的方法
Cat cat = new Cat("咪咪");
cat.eat(); // 调用父类的
方法
cat.meow(); // 调用子类自己的方法
}
}
继承中的细节
-
子类继承所有的属性和方法,非私有的属性和方法可以在子类直接访问,当私有属性和方法不能在子类中直接访问,要通过父类提供公共的方法去访问
public class Aminal { private String name; private int age; public Aminal(String name, int age){ this.name = name; this.age = age; } public int Getage(){ return this.age; } } public class Dog extends Aminal{ public Dog(String name, int age) { super(name, age); } public void print(){ // this.name 调用不了父类的属性 this.Getage();//调用父类的公共方法来获取age属性 } }
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的那个构造器,默认情况下总会调用父类的无参构造器(默认是super();),如果父类没有提供无参构造器,则必须在子类中用
super
去指定使用哪个构造器来初始化父类。 -
如果希望指定去调用某个构造器,则显式的调用:
**supe**r
(参数列表); -
super
在使用时,必须放在构造器第一行(super
只能在构造器中使用) -
this
和supe
r不能同时存在 -
Java所有的类都是Object类的子类
-
父类构造器的调用不限于直接父类,将一直追溯直到Object类(顶级父类)
-
子类最多继承一个父类(指直接继承),即Java是单继承机制
-
继承不能滥用,子类和父类之间必须满足 is - a 的logic关系(person is a Music ?)
public class Pc extends Computer{
private String brand;
public Pc(String cpu, String disk, int storge, String brand) {
//这里IDEA,根据继承的规则,自动生成
//体现继承的设计思想,父类的构造器完成父类属性的初始化
//子类的构造器完成子类属性的初始化
super(cpu, disk, storge);
this.brand = brand;
}
public String print(){
return this.getDetails() + "品牌" + this.brand;
}
}
super关键字
概念:super
代表父类的引用,用于访问父类的属性,成员方法,构造器
基本语法:
-
访问父类的属性,但是不能访问父类的
private
属性 -
方法同1
-
访问构造器,第一行
super带来的便利
- 分工明确,父类由父类初始化,子类由子类初始化
- 当子类中出现与父类重名的成员时,与使用
this
,super
,直接访问的效果一样
细节:super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以去用super去访问爷爷类的成员,如果多个上级类(基类)都有同名的成员,使用super时遵循就近原则,(**基类有被重写的方法,父类同名方法将访问不了)**this同样
重写
概念:
方法重写(覆盖)就是子类有一个方法,和父类(爷爷类也行)的某个方法的**名称****,返回类型**,参数一样,那么我们就说这个方法覆盖了父类的那个方法
class Animal {
// 父类的makeSound方法
void makeSound() {
System.out.println("动物在叫~");
}
}
class Dog extends Animal {
// 子类重写makeSound方法
@Override
void makeSound() {
System.out.println("狗在叫~");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
myAnimal.makeSound(); // 输出: Some sound
Dog myDog = new Dog();
myDog.makeSound(); // 输出: Bark
// 使用父类类型的引用指向子类对象
Animal myPet = new Dog();
myPet.makeSound(); // 输出: Bark
}
}
细节:
-
子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
-
子类方法的返回类型和父类方法的返回类型一样,或是父类返回类型的子类
-
子类方法不能缩小父类方法的权限
多态
Java中的多态是面向对象编程中的一个重要概念,它指的是一个对象可以有多种形态。换句话说,一个引用类型(比如父类或者接口类型的引用)在运行时可以指向多种不同的实例对象,调用的具体方法由对象的实际类型决定。
方法的多态
通过重写,重载来体现
1,重载(体现了编译时多态。)
在编译时,编译器根据代码中方法调用的参数类型来决定要调用的方法版本,这种绑定是在编译期完成的,因此称为编译时多态。这种多态是由方法的重载实现的,即同一个类中出现了多个方法名相同但参数列表不同的方法。在编译时,编译器会根据方法调用时提供的参数类型来静态地解析调用哪个方法版本。
编译时多态保证了程序在编译时就可以确定具体要调用的方法,提高了程序的效率。这与运行时多态(动态多态)形成对比,动态多态是在程序运行时根据对象的实际类型来确定调用的方法版本,这种绑定是在运行期完成的。
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
System.out.println(calculator.add(1, 2)); // 输出:3
System.out.println(calculator.add(1.5, 2.5)); // 输出:4.0
}
}
2,重写(体现了运行时多态)
在Java中,运行时多态主要是通过方法的覆盖(重写)来实现的。当子类重写了父类的方法后,通过父类引用调用这个方法时,实际调用的是子类中重写后的方法,而不是父类的方法。这种动态绑定是在程序运行时根据对象的实际类型来确定的,因此称为运行时多态。
运行时多态使得程序在运行时能够根据对象的实际类型进行动态绑定,从而使得程序更加灵活和具有扩展性。这种多态性使得代码更具有通用性,能够适应多种不同的场景,提高了代码的可维护性和可扩展性。
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("狗发出汪汪的声音");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("猫发出喵喵的声音");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // 输出:狗发出汪汪的声音
animal2.makeSound(); // 输出:猫发出喵喵的声音
}
}
对象的多态
必背的几句话
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的
(4)编译类型看定义时 = 号的左边,运行类型看 = 号的右边
多态----向上转型
多态的前提:两个对象(类)存在继承关系
- 本质:父类对象的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边。可以调用父类的所有成员(需遵守访问权限),不能调用子类的特有成员(在编译阶段,能调用那些成员,是由编译类型决定),最终的运行效果看子类的具体实现(规则与方法的调用规则一样)
多态----向下转型
-
本质:只能强转父类的引用,而不是父类的对象
-
语法:子类类型 引用名 = (子类类型)父类引用;
-
要求:父类的引用必须指向的是当前目标类型的对象
-
特点:可以调用子类类型中的所有成员
细节
属性没有重写一说,直接看编译类型
instanceof
在Java中,instanceof
关键字是用于检查对象是否为特定类或其子类的实例的。它主要用于类型检查和避免在向下转型时发生 ClassCastException
。
class Animal {}
class Dog extends Animal {}
public class Test {
public static void main(String args[]) {
Animal a = new Animal();
Dog d = new Dog();
//用于判断对象的运行类型是否是XX类型或XX类型的子类型
System.out.println(a instanceof Animal); // 输出 true
System.out.println(d instanceof Animal); // 输出 true,因为 Dog 是 Animal 的子类
System.out.println(a instanceof Dog); // 输出 false,因为 a 不是 Dog 的实例
}
}
动态绑定机制
- 当运行对象方法时,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有绑定机制,哪里声明,哪里使用
- !!! 调方法看运行类型,调属性看编译类型 !!!
多态的运用
1,多态数组
多态数组,需要满足数组类型定义为父类类型,而里面保存的实际元素类型为子类类型(可同时存放多种子类)。实际达到的效果就是父类引用指向子类对象。
class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
class Dog extends Animal {
public void makeSound() {
System.out.println("Bark!");
}
}
class Cat extends Animal {
public void makeSound() {
System.out.println("Meow!");
}
}
public class PolymorphicArrayExample {
public static void main(String[] args) {
// 创建一个Animal类型的数组,可以存储Dog和Cat对象
Animal[] animals = new Animal[3];
// 分别创建Dog和Cat对象
Dog dog = new Dog();
Cat cat = new Cat();
// 将Dog和Cat对象存储到数组中
animals[0] = dog;
animals[1] = cat;
// 通过父类引用调用makeSound方法,实现动态绑定
for (Animal animal : animals) {
animal.makeSound();
}
}
}
2,多态参数
方法的形参类型定义为父类类型,当传入的实参类型为子类类型时,每传入一个子类对象,都相当于形成了一次多态(父类引用指向子类对象)。
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
public void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle extends Shape {
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class PolymorphicParameterExample {
// 接受Shape类型的参数
public static void drawShape(Shape shape) {
// 调用传入对象的draw方法,实现动态绑定
shape.draw();
}
public static void main(String[] args) {
// 创建Circle和Rectangle对象
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
// 调用方法,传入不同类型的对象
drawShape(circle); // Drawing a circle
drawShape(rectangle); // Drawing a rectangle
}
}