目录
本文内容使用IDEA集成开发工具,版本2020.2
idea常用快捷键
- 删除当前行,自己配置 ctrl + D (file-settings-keymap-delete line)
- 复制当前行,自己配置 ctrl + alt + 向下键
- 补全代码 alt + /
- 添加和取消注释 ctrl + /
- 导入该行所需的类,先配置auto import,配置后使用 alt + enter 即可
- 快速格式化代码 ctrl + alt + L
- 快速运行程序,自己配置 ctrl + R
- 生成构造器 alt + insert (常用)
- 查看一个类的层级关系 ctrl + H
- 定位到方法,光标移动到方法处,ctrl + B
- 自动分配变量名,在new 类型后输入.var回车
idea模板
file-settings-editor-Live Templates
如:
main:输入main回车可以快速补齐输出模板
包
- 作用
- 区分相同名字的类
- 类很多时方便管理
- 控制访问范围
- 包的本质
实质上就是创建不同的文件夹来保存类
- 包的命名规则
- 只能包含数字、字母、下划线、小圆点,不能以数字开头
- 小圆点间隔的每个部分都要符合1的规则
- 一般由小写字母和小圆点组成
- 常用的包
- java.lang.* //lang是基本包,默认引入,无需额外引入
- java.util.* //util包,系统提供的工具包,java.util.Scanner
- java.net.* //网络包,网络开发
- java.awt.* //java的界面开发,GUI
- 包的使用细节
- package用来声明当前类所在的包,位于类的最上面,一个类只能有一个package
- import导入包,位于package下面,类上面,可以有多个导入,无顺序
访问修饰符
- public
- protected
- 默认
- private
背!
- 修饰符使用细节
类的修饰符只能是 public 或 默认不写
封装 (encapsulation)
隐藏实现细节,将抽象出的属性和方法封装起来
- 封装的实现步骤
- 将属性进行私有化
- 提供一个公共的set方法,用于对属性判断并赋值
- 提供一个公共的get方法,用于获取属性的值
public class Encapsulation01 {
public static void main(String[] args) {
Person person = new Person();
person.setName("Lucy");
person.setAge(21);
person.setSalary(8000);
System.out.println(person.info());
Person person1 = new Person();
person1.setName("L");
person1.setAge(0);
person1.setSalary(6000);
System.out.println(person1.info());
}
}
class Person {
public String name;
private int age;
private double salary;
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 6) {
this.name = name;
}else{
System.out.println("名字长度应为2-6个字符,默认值为Lulu");
this.name = "Lulu";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 1 && age <= 120){
this.age = age;
}else{
System.out.println("年龄应在[1-120]内,给出默认值:18");
this.age = 18;
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String info() {
return "name = " + name + " age = " +
age + " salary = " + salary;
}
}
也可以将set方法写在构造器中,也可以验证
public Person(String name, int age, double salary) {
setName(name);
setAge(age);
setSalary(salary);
}
- 封装小练习
public class Account {
private String name;
private String password;
private double balance;
public Account() {
}
public Account(String name, String password, double balance) {
setName(name);
setPassword(password);
setBalance(balance);
}
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 4) {
this.name = name;
}else {
System.out.println("名字长度应该在2-4,默认值Lili");
this.name = "Lili";
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
if(password.length() == 6) {
this.password = password;
}else {
System.out.println("密码长度应为6位,默认123456");
this.password = "123456";
}
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
if(balance >= 20) {
this.balance = balance;
}else {
System.out.println("余额应该大于20,默认值10000");
this.balance = 10000;
}
}
public void showInfo() {
System.out.println("name = " + name + " password = "
+ password + " balance = " + balance);
}
}
public class TestAccount {
public static void main(String[] args) {
Account account = new Account("l", "111111", 10);
account.showInfo();
}
}
继承
当多个类存在多个相同的属性和方法时,就可以抽象出一个父类,实现代码复用 ,所有子类不需要重新定义这些方法,只需extends父类
- 基本语法
class 子类名 extends 父类名 {
}
- 继承细节
- 父类的非私有属性和方法可以在子类直接访问,私有属性和方法只能通过父类的共有方法来访问
- 子类必须调用父类的构造器,完成父类的初始化
- 创建子类对象时,不管使用子类的哪个构造器,默认都要调用父类的无参构造器,若父类没有无参构造器,则必须指定一个父类构造器,否则报错
- 指定调用父类构造器,super(形参列表)
- super()必须位于构造器内第一行
- super()和this()都必须位于第一行,所以两个方法不能共存在一个构造器
- java所有类都是object的子类
- 父类构造器的调用将一直层层追溯到object类
- 子类最多只能继承一个父类
super关键字
super用于访问父类的属性、方法和构造器,但不能访问父类private的属性和方法。
- 细节
- 调用父类构造器好处:父类属性可以由父类初始化,子类属性由子类初始化
- 当子类中的属性或方法与父类的重名时,像调用父类的必须用super关键字,若没有重名,super等价于this
方法重写 (override)
- 方法重写也叫方法覆盖,是指子类的方法名、参数列表和父类完全一样
- 子类方法的返回类型和父类的一样,或是父类返回类型的子类
- 子类方法不能缩小父类的返回权限(public>protected>默认>private)
- 方法重写小练习
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say() {
return "name = " + name + " age = " + age;
}
}
public class Student extends Person{
private int id;
private double score;
public Student(String name, int age, int id, double score) {
super(name, age);
this.id = id;
this.score = score;
}
@Override
public String say() {
return "Student " + super.say() + " id = "
+ id + " score = " + score;
}
}
public class TestOverride {
public static void main(String[] args) {
Person june = new Person("june", 21);
Student july = new Student("July", 20, 11, 98.5);
System.out.println(june.say());
System.out.println(july.say());
}
}
多态
方法和对象具有多种形态
- 方法的多态,重写和重载体现多态
- 对象的多态
- 一个对象的编译类型和运行类型可以不一致⭐⭐
- 编译类型在定义对象的时候就确定了,不能改变
- 运行类型可以变化
- 编译类型看定义时=号的左边,运行类型看=号的右边
多态细节
- 多态的前提是两个类存在继承关系
- 多态的向上转型
- 父类的引用指向了子类的对象
- 基本语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,编译类型看右边
- 可以调用父类的所有成员,不能调用子类的特有成员,因为编译类型是父类,没有子类的特有方法
- 多态的向下转型
- 基本语法:子类类型 引用名 = (子类类型) 父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向当前要转型的子类类型的对象
- 可以调用子类类型的所有成员
- 属性没有重写之说,属性的值看编译类型⭐⭐
public class PolyDetail {
public static void main(String[] args) {
Base base = new Sub(); //向上转型
System.out.println(base.count); //编译类型为Base,count = 10
Sub sub = (Sub)base; //向下转型
System.out.println(sub.count);// 编译类型为Sub,count = 20
}
}
class Base {
int count = 10;
}
class Sub extends Base{
int count = 20;
}
instanceOf比较操作符
用于判断某类型是否为xx类型或xx类型的子类型
- instanceOf 看的是运行类型
public class PolyDetail02 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
System.out.println(a1 instanceof A); //true
System.out.println(a1 instanceof B); //false
System.out.println(a2 instanceof B); //true
}
}
class A {
}
class B extends A{
}
小练习
动态绑定机制 (很很很重要)
- 当调用对象方法时,方法会和对象的内存地址即运行类型绑定
- 对象属性没有动态绑定机制,哪里声明,哪里使用
下面两段代码值得好好思考~
package com.lili.poly_;
public class Dynamic {
public static void main(String[] args) {
AA aa = new BB();
System.out.println(aa.sum()); //40
System.out.println(aa.sum1()); //30
}
}
class AA {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class BB extends AA {
public int i = 20;
public int sum() {
return i + 20;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
package com.lili.poly_;
public class Dynamic {
public static void main(String[] args) {
AA aa = new BB();
System.out.println(aa.sum()); //30
System.out.println(aa.sum1()); //30
}
}
class AA {
public int i = 10;
public int sum() {
return getI() + 10; //BB类没有sum方法,往父类AA来找,这里的getI方法有动态绑定,调用的是BB的getI
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class BB extends AA {
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
思考,如果把BB类的sum1方法也注释了,会输出什么?
多态数组
数组的定义类型为父类类型,里面保存的元素类型可以为父类或子类
多态参数
方法定义的形参类型为父类,实参可以是它的子类
== 和 equals 比较
- == 既可以判断基本类型,又可以判断引用类型
- == 判断基本类型时,判断的是值是否相等
- == 判断引用类型时,判断的是是否是同一个对象,也可以说是否指向同一地址
- equals 是Object 的方法,只能判断引用类型
- Object 子类String 和 Integer 重写了equals方法,判断时值相等就相等
hashCode方法
- 提高具有hashCode结构的容器的效率
- 两个引用如果指向同一个对象,hashCode值一定是一样的
- 两个引用如果指向不同对象,hashCode值不一样
- hashCode值是根据地址得到的,但hashCode值不完全等同于地址
toString方法
- 默认返回:全类名 + @ + 哈希值的十六进制,Object的子类往往会重写 toString 方法
- 当直接输出一个对象时,toString方法会默认被调用
finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收,当某个对象没有任何引用时,jvm认为这个对象是一个垃圾对象,就会使用垃圾回收机制销毁该对象,在销毁该对象前会调用finallize方法
- 垃圾回收机制的调用由系统决定,也可以通过System.gc()主动触发垃圾回收机制
断点调试 (Debug)
- F8逐行执行
- F7跳入方法内部,光标放到某个变量上,可以看到当前值
- 断点可以在Debug的过程中,动态的下断点