每一个强者的自由,都应该以弱者的自由为边界。
1.包
1.1 为什么要有包这个概念
现在有两个程序员共同开发一个Java项目,程序员甲要定义一个类Dog,程序员乙也想要定义一个类Dog。两个类只是名字相同,实际的业务处理是不一样的。现在要怎么办呢?打一架,赢了的有定义权吗?
1.2 包的作用
- 区分相同名字的类
- 当项目非常大,定义的类非常多时,可以有条理,有层次地管理类
- 控制各个类的访问范围
1.3 包的基本语法
package com.example.controller;
// 说明
- package:java关键字,表示打包
- com.example.controller:表示定义的包名
1.4 包的本质
实际上就是创建不同的文件夹/目录来保存类文件。(类似Windows的目录结构)
1.5 常用的包
java.lang.* //lang 包是基本包,默认引入
java.util.* //util 包,系统提供的工具包, 工具类
java.net.* //网络包,网络开发
java.awt.* //是做 java 的界面开发,GUI
1.6 包的引入
// 引入单个类
import java.util.Scanner;
// 引入一个包下面的所有类
import java.util.*;
1.7 使用注意
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package。
- import 位置放在package的下面,在类定义的前面,可以有多句且没有顺序要求。
2.访问修饰符
java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public | 可以 | 可以 | 可以 | 可以 |
受保护 | protected | 可以 | 可以 | 可以 | 不可以 |
默认 | 没有修饰符 | 可以 | 可以 | 不可以 | 不可以 |
私有 | private | 可以 | 不可以 | 不可以 | 不可以 |
一般就使用 public(哪里的都都可以访问) 和 private(只有本类的可以访问)。
3.面向对象编程 - 封装
把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据就被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。
好处
- 隐藏实现的细节
- 可以对数据进行验证,保证安全合理
最典型的例子就是,定义私有的属性,通过get、set方法去操作属性值。
封装的实现步骤(三步)
1.将属性进行私有化private(不能直接修改属性)
2.提供一个公共的(public)set 方法,用于对属性判断并赋值
public void SetXxx(类型 参数名) {
// 加入数据验证的业务逻辑
属性 = 参数名;
}
3.提供一个公共的(public)get 方法,用于获取属性的值
public 数据类型 getXxx() {
return xx;
}
4.面向对象编程 - 继承
继承可以解决代码复用,让我们的编程更加靠近人类思维。多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类。在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来 声明继承父类即可。
继承的基本语法
class 子类 extends 父类 {
}
// 说明
1. 子类就会自动拥有父类定义的属性和方法
2. 父类又叫超类、基类
3. 子类又叫派生类
好处
1) 代码的复用性提高了
2) 代码的扩展性和维护性提高了
注意
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。
- 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系。
方法重写/覆盖(override)
方法重写就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
使用情况:当父类中提供的方法已经不满足子类使用时,子类就通过重写的方法,重新写子类需要这个方法的逻辑。
5.super关键字
super 代表父类的引用(this代表本对象的引用),用于访问父类的属性、方法、构造器。
基本语法
1.访问父类的属性,但不能访问父类的private属性
super.属性名;
2.访问父类的方法,但不能访问父类的private方法
super.方法名(参数列表)
3.访问父类的构造器
super(参数列表); //只能放在构造器的第一句,只能出现一句
6.面向对象编程 - 多态
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的前提是:两个对象(类)存在继承关系。
6.1 方法的多态(方法重载)
代码示例
public class HelloWorld {
public static void main(String[] args) {
// 方法重载体现多态 - 传入不同的参数,就会调用不同 sum 方法
A a = new A();
System.out.println(a.sum(10, 20));
System.out.println(a.sum(10, 20, 30));
}
}
class A {
/**
* 计算两个整数的和
*/
public int sum(int n1, int n2) {
return n1 + n2;
}
/**
* 计算三个整数的和
*/
public int sum(int n1, int n2, int n3) {
return n1 + n2 + n3;
}
}
6.2 对象的多态(核心)
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以改变的(改变引用的指向)
- 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
// 使用多态机制,可以统一的管理主人喂食的问题
// animal 编译类型是 Animal,可以指向(接收) Animal 子类的对象
// food 编译类型是 Food ,可以指向(接收) Food 子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());
}
6.3 多态的向上/向下转型
向上转型
1.本质:父类的引用指向了子类的对象。
2.语法:父类类型 引用名 = new 子类类型();
3.特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限),子类中的特有成员不能调用,因为编译类型在代码运行时已经固定。
最终运行效果看子类的具体实现
向下转型
1.语法:子类类型 引用名 = (子类类型)父类引用;
2.只能强转父类的引用,不能强转父类的对象
3.要求父类的引用必须指向的是当前目标类型的对象
4.当向下转型后,可以调用子类类型中所有的成员
代码示例
public class HelloWorld {
public static void main(String[] args) {
// 向上转型: 父类的引用指向了子类的对象
Animal animal = new Cat();
animal.eat(); //猫吃鱼
animal.show(); //hello,你好
animal.sleep(); //睡
//animal.catchMouse();错误
// 多态的向下转型 - 可以调用子类中特有的方法
Cat cat = (Cat) animal;
cat.catchMouse(); //猫抓老鼠
}
}
class Animal {
String name = "动物";
int age = 10;
public void sleep() {
System.out.println("睡");
}
public void run() {
System.out.println("跑");
}
public void eat() {
System.out.println("吃");
}
public void show() {
System.out.println("hello,你好");
}
}
class Cat extends Animal {
// 重写父类方法
public void eat() {
System.out.println("猫吃鱼");
}
// Cat 子类中特有的方法
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
7.Object 类详解
7.1 equals方法
== 和 equals 对比
- ==:既可以判断基本类型(判断的是值是否相等),也可以判断引用类型(判断的是地址是否相等,即是不是同一个对象)。
- equals:是Object类中的方法,只能判断引用类型,即判断的是地址是否相等,子类中往往会重写该方法,用于判断内容是否相等。
public static void main(String[] args) {
// 基本数据类型,判断值是否相等
int num1 = 10;
double num2 = 10.0;
System.out.println(num1 == num2);
// 引用数据类型,判断地址是否相等
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);
System.out.println(str1.equals(str1));
}
输出结果
true
true
true
查看String重写的equals源码
/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*
* @param anObject
* The object to compare this {@code String} against
*
* @return {@code true} if the given object represents a {@code String}
* equivalent to this string, {@code false} otherwise
*
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
7.2 toString方法
1) 默认返回:全类名+@+哈希值的十六进制
2) 重写 toString 方法,返回的是对象的属性信息
代码示例
public class HelloWorld {
public static void main(String[] args) {
// 不重写toString方法,默认返回:@全类名 + @ + 十六进制的哈希值
Person person = new Person("路明非", "男", 18);
System.out.println(person.toString());
// 重写toString方法,返回的是对象的属性信息
Cat cat = new Cat("小花", "女", 3);
System.out.println(cat.toString());
}
}
class Person {
private String name;
private String sex;
private int age;
public Person(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
}
class Cat {
private String name;
private String sex;
private int age;
public Cat(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
输出
Person@4554617c
Cat{name='小花', sex='女', age=3}
JDK的toString方法
/**
* Returns a string representation of the object. In general, the
* {@code toString} method returns a string that
* "textually represents" this object. The result should
* be a concise but informative representation that is easy for a
* person to read.
* It is recommended that all subclasses override this method.
* <p>
* The {@code toString} method for class {@code Object}
* returns a string consisting of the name of the class of which the
* object is an instance, the at-sign character `{@code @}', and
* the unsigned hexadecimal representation of the hash code of the
* object. In other words, this method returns a string equal to the
* value of:
* <blockquote>
* <pre>
* getClass().getName() + '@' + Integer.toHexString(hashCode())
* </pre></blockquote>
*
* @return a string representation of the object.
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
7.3 finalize方法
1) 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作。
2) 什么时候被回收:当某个对象没有任何引用时,jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法。
3) 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制。
在实际开发中,几乎不会运用 finalize (都默认交给虚拟机自动处理), 所以更多就是为了应付面试。
代码示例
public class HelloWorld {
public static void main(String[] args) {
Car bmw = new Car("宝马");
bmw = null;
System.gc(); //主动调用垃圾回收器
System.out.println("程序退出了....");
}
}
class Car {
private String name;
public Car(String name) {
this.name = name;
}
// 重写finalize
@Override
protected void finalize() throws Throwable {
System.out.println("销毁汽车" + name );
System.out.println("释放了某些资源...");
}
}