1、包
1.1 包的作用
- 包可以区分名字相同的类
- 方便管理类
- 可以用于访问权限的控制
1.2 包的基本语法
//打包
package 关键字;
//包名
com.名字;
复制代码
实例,最长见的就是在idea中创建文件时,上面写的:
package com.oliver;
import java.util.Date;
复制代码
1.3 常用的包
- java.lang.* : lang包是基本包,默认引入,不需要再引入
- java.util.* : util包,系统提供的工具包, 工具类,使用Scanner
- java.net.* : 网络包,网络开发
- java.awt.* : 是做java的界面开发,GUI
引入包可以使用import,可以引入一个类,也可以引入包内全部的类
2、访问修饰符
之前提到过,类、方法或者参数可以设置访问级别,用于封装避免其他人使用,一共有下面四种:
- 公开级别:用public修饰,对外公开
- 受保护级别:用protected修饰,对子类和同一个包中的类公开
- 默认级别:没有修饰符号,向同一个包的类公开
- 私有级别:用private修饰,只有类本身可以访问,不对外公开
可以这样记忆,把它们都想成钱:
- public就是慈善基金,给大家用的;
- protected受保护的钱,就是遗产,自己、妻子(同包)、孩子(子类)可以使用;
- default默认孩子要成家,家里就自己和妻子,这就是默认用的钱
- private就是自己的小金库
可以修饰类的默认和public,其他不能。
自行测试,代码如下:
测试类A:
package com.oliver;
/**
* @author :16140
* @description :
* @create :2022-08-07 14:45:00
*/
public class A {
//测试属性
public String name1 = "张三";
protected String name2 = "李四";
String name3 = "王五";
private String name4 = "赵六";
public static void main(String[] args) {
A a = new A();
a.test1();
}
public void test1(){
System.out.println(name1);
System.out.println(name2);
System.out.println(name3);
System.out.println(name4);
}
}
复制代码
同包下创建B:
public class B {
public static void main(String[] args) {
A a = new A();
System.out.println(a.name1);
System.out.println(a.name2);
System.out.println(a.name3);
//System.out.println(a.name4); //不能访问
}
}
复制代码
不同包下的子类,其实直接访问A对象还只能访问public,但是如果使用看继承过来的B对象,就可以访问public和protected了:
package com.oliver.test2;
import com.oliver.test1.A;
/**
* @author :16140
* @description :
* @create :2022-08-07 15:00:00
*/
public class C extends A {
public static void main(String[] args) {
C c = new C();
System.out.println(c.n1);
System.out.println(c.n2);
}
}
复制代码
不同包也不继承:
package com.oliver.test2;
import com.oliver.test1.A;
/**
* @author :16140
* @description :
* @create :2022-08-07 15:08:00
*/
public class D {
public static void main(String[] args) {
A a = new A();
System.out.println(a.n1);
}
}
复制代码
3、面向对象三大特征
封装、继承和多态。
- 封装:开发者把数据保护在程序内部,只给使用者授权操作的方法
- 继承:子类可以继承父类,这样就避免了重复创建类似的类
- 多态:创建父类型的对象,指向子类型,程序在编译的时候可以根据子类型去自动创建相对的对象
3.1 封装
将属性和一些方法私有化(private),由公共方法去调用。
下面的代码是不能再直接设置值了,不太安全,这里属性私有化,提供了公开的set和get方法。
public class Encapsulation01 {
public static void main(String[] args) {
//Person person = new Person("z",1); 不可以这样做了
Person person = new Person();
person.setName("张三");
person.setAge(20);
System.out.println("我是" + person.getName() + ",今年" + person.getAge());
}
}
class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
复制代码
3.2 继承
继承主要体现的是两个类的共同点,多态体现的是两个类的不同点。
比如创建一个银行卡,里面包含属性姓名,年龄,金额,再创建一个储蓄卡,同样是姓名,年龄,金额,这样就冗余了,不如提取出一个卡类,包含姓名,年龄,金额三个属性,让这两种卡继承这个类。
public class Extends01 {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
bankCard.setName("张三");
bankCard.setAge(20);
bankCard.setAmount(10000);
System.out.println(bankCard.getMoney(100));
CashCard cashCard = new CashCard();
cashCard.setName("李四");
cashCard.setAge(23);
cashCard.setAmount(5000);
System.out.println(cashCard.getMoney(300));
}
}
//card类
class Card{
public String name;
public int age;
public int amount;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getMoney(int count){
int remain = getAmount() - count;
setAmount(remain);
return getAmount();
}
}
class BankCard extends Card{
}
class CashCard extends Card{
}
复制代码
细节:
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译
- 如果希望指定去调用父类的某个构造器,则显式的调用一下super(),super在使用时,必须放在构造器第一行
- super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- Java是单继承
- 子类和父类必须满足is-a的关系才能使用继承,不能滥用
public class Extends02 {
public static void main(String[] args) {
//系统会默认让子类去使用父类的无参构造,所以要想让下面生效,必须使用super(带参)
Cat cat = new Cat("xiaomao",20);
}
}
class Animal{
private String name;
private int weight;
public Animal() {
}
public Animal(String name, int weight) {
this.name = name;
this.weight = weight;
}
}
class Cat extends Animal{
public Cat(String name, int weight) {
//this(); this和super都必须在第一行,所以不能一起使用
super(name, weight);
}
}
复制代码
3.3 方法重写
既然可以使用继承,总不能所有子类都必须使用父类一样的方法吧,其实可以让子类去继承父类,然后修改或增加方法,完成功能的扩展,修改方法叫做方法重写或方法覆盖。
这个注意不要和方法重载搞混。
方法覆盖是书红:子类和父类方法名、返回值类型相同、参数也一样,这样就是方法覆盖,简单点理解,就是觉得父类的这个方法不好用,自己弄了个新的。
public class Override01 {
public static void main(String[] args) {
EnglishTeacher teacher1 = new EnglishTeacher("王老师");
System.out.println(teacher1.teach(",英语流利的一批"));
MathTeacher teacher2 = new MathTeacher("柳老师");
System.out.println(teacher2.teach(",数学就没有我不会的"));
}
}
class Teacher{
public String name;
public Teacher() {
}
public Teacher(String name) {
this.name = name;
}
public String teach(String introduction){
return "大家好,我是" + this.name + introduction;
}
}
class EnglishTeacher extends Teacher{
public EnglishTeacher(String name) {
super(name);
}
@Override
public String teach(String introduction) {
return "大家好,我是英语老师" + super.name + introduction;
}
}
class MathTeacher extends Teacher{
public MathTeacher(String name) {
super(name);
}
@Override
public String teach(String introduction) {
return "大家好,我是数学老师" + super.name + introduction;
}
}
复制代码
3.4 多态
仅仅能继承 + 覆盖就可以了吗?可以想象一下这样一个场景,你创建了一个类,里面写了很多行代码,而且这个类的方法和参数被无数个地方调用,这个时候这个类要被替换成另一个类,难道你要把所有的引用名字都替换一遍吗?
当然不是,这个其实是后面设计模式会详细介绍的一点,假如说你拿了个电视的遥控器去操作电视,如果想操作空调的话就只能拿起空调遥控,那么有没有可能这个遥控器能打开很多的电器设备,不需要换遥控器(引用呢),这里的多态也可以满足这个功能,主要记住一点就是父类型引用指向子类型对象。
语法如下:
父类型 父类变量名 = new 子类型1();
父类型 父类变量名 = new 子类型2();
复制代码
比如这个例子:
public class PloyMethod {
public static void main(String[] args) {
A a1 = new B();
A a2 = new C();
a1.sayHello();//hello B
a2.sayHello();//hello C
}
}
class A{
public void sayHello(){
System.out.println("hello A");
}
}
class B extends A{
@Override
public void sayHello() {
System.out.println("hello B");
}
}
class C extends A{
@Override
public void sayHello() {
System.out.println("hello C");
}
}
复制代码
对象在编译器是不会去确定具体的类型的,只看父类,而运行时,是通过子类型去确定具体是那种类型的对象。(动态绑定机制,Java有这样的机制,并不是所有语言都有)
这里注意类型引用和具体对象类型不一样时,需要向下或者向上转型,向上转型还好说,不会出现什么问题,向下转型需要强转。比如我说一只猫是一个动物是对的,一个动物是一只猫就不一定对了,所以强转需谨慎。
4、Object类
4.1 equals方法
基本数据类型通过"=="比较可以得到正确的值,比如2==2,但是引用类型就不一定了,因为它比较的是内存地址,如果参数都相同的两个对象,它们的内存地址不一定一样,使用"=="去判断就不合适了。
那么可以使用equals方法,其实equals判断的 也是对象是否地址一致,不过我们学了方法重写,父类型Object的equals方法,在子类型里可以重写其方法。
比如我们看看String类的equals方法就重写了,源码如下:
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;
}
复制代码
我们也可以自己写一个equals方法,比如要比较银行账户和自己输入的用户名密码是否一致,那肯定要有一个账户,如果判断这个账户对象和自己是否一致,如果equals没重写那肯定基本不会通过验证了,所以要重写。
package com.oliver.obj;
import java.util.Objects;
/**
* @author :16140
* @description :
* @create :2022-08-07 16:51:00
*/
public class Person {
private String name;
private String password;
public Person(String name, String password) {
this.name = name;
this.password = password;
}
public static void main(String[] args) {
Person person1 = new Person("张三","123456");
Person person2 = new Person("张三","123456");
System.out.println(person1 == person2); //false
System.out.println(person1.equals(person2));//true
}
public boolean equals(Object obj){
//如果引用一致,那肯定是一个对象
if (this == obj) return true;
//如果二者类型都不一致,肯定不是一个对象
if (obj == null || this.getClass() != obj.getClass()) return false;
//二者不相同,而且不是null,类型也一致,就可以强转了
Person person = (Person) obj;
return Objects.equals(name,person.name) && Objects.equals(password,person.password);
}
}
复制代码
4.2 hashCode方法
返回这个对象哈希码值,使用这个方法可以提高性能。
String类中的hashCode方法:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
复制代码
比较两个对象的时候,如果先比较hashcode,一致了再比较其他能提高很多效率。
4.3 toString方法
默认返回值是:全类名+@+哈希值的十六进制
比如上一个person1的值:com.oliver.obj.Person@1b6d3586
我们可能希望输出更好的结果,就需要重写toString方法,这个不必要介绍看一下idea给我们重写的toString方法实例即可:
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Person{");
sb.append("name='").append(name).append('\'');
sb.append(", password='").append(password).append('\'');
sb.append('}');
return sb.toString();
}
复制代码
4.4 finalize方法
- 当对象被回收时,系统会自动调用该对象的finalize方法,子类可以重写该方法
- 当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来 销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制
public class FinalizeTest {
public static void main(String[] args) {
Rubbish rubbish = new Rubbish();
rubbish = null;
System.gc(); //不使用它的时候,测试很多回没有被回收
//添加了以后很快就打印了垃圾被回收了
System.out.println("回收完毕");
}
}
class Rubbish{
public Rubbish() {
}
@Override
protected void finalize() throws Throwable {
System.out.println("垃圾被回收了");
super.finalize();
}
}
复制代码
4.5 Idea工具使用
debug的几个按键介绍:
- F7(跳入) :跳入方法内
- F8(跳过) :逐行执行代码
- shift+F8(跳出)
- F9(resume,执行到下一个断点)