Java学习记录-1

用于Java学习记录

IDEA快捷键/模板快捷键

COMMAND TODO
alert+insert 自动生成构造方法
ctrl+h 查看类的层级关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class 类名称 {
属性 (变量) ;
行为 (方法) ;
}
//class Example:
class Person {
String name ;
int age ;
public void get() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
对象.属性:表示调用类之中的属性;
对象.方法():表示调用类之中的方法。

构造器/构造方法

1
2
3
4
5
6
7
new一个对象时,就会调用构造器,完成对象属性的初始化
结构如下:
[修饰符,比如public] 类名 (参数列表,可以没有参数){
//这里不能有return
}
1.方法名和类名必须一致
2.构造器无返回值

默认构造器

1
2
3
4
5
6
如果没有定义构造器,则会默认一个无参构造器,这就是为什么你定义了一个对象,比如 People,没有定义任何构造器却可以new这个对象,比如 new People() 。如果自定义了构造器,则会覆盖默认构造器。
public class People {
public People() {

}
}

禁止对象被外部创建

1
2
3
4
5
6
不希望定义的对象被外部创建(典型的就是单例了),那直接将构造器的修饰符改为 private 即可。这样就不能在外部通过new来创建这个对象了。
public class People {
private People(){

}
}

构造器重载

1
2
3
4
5
6
7
8
9
10
public class People {
//通过new People()调用
public People(){

}
//通过new People("字符串") 调用
public People(String str){

}
}

this

1
this 是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。

直接引用

1
this 相当于是指向当前对象本身

形参与成员名字重名,用this来做区分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
private int age = 10;
public Person(){
System.out.println("初始化年龄:"+age);
}
public int GetAge(int age){
this.age = age;
return this.age;
}
}

public class test1 {
public static void main(String[] args) {
Person Harry = new Person();
System.out.println("Harry's age is "+Harry.GetAge(12));
}
}
初始化年龄:10
Harry's age is 12

类加载

创建实例对象时(new)

创建子类实例时,父类也会被加载

使用类的静态成员(静态方法、静态属性)

1
2
3
4
本质:通过创建不同的文件夹/目录来保存类文件
package com.todo
package--->关键字
com.todo--->包名

作用

1
2
3
区分相同名字的类
更好的管理类
控制访问范围

访问修饰符

Example TODO
public 对外公开
proctected 对子类和同一个包中的类公开
默认 向同一个包中的类公开
private 只有类本身可以访问,不对外公开

继承

1
2
3
4
格式:
class 子类 extends 父类{

}

Example:

1
2
3
4
5
6
7
8
9
10
公共类student
public class student{
public String name;
int age;
double score;

public void showInfo(){
System.out.println("学生:"+name+" 年龄:"+age+"scroe:"+score);
}
}
1
2
3
4
5
6
7
子类:小学生
public class pupil extends student{
//在这里就可以写小学生自己的方法 such as
public void testing(){
System.out.println("小学生"+name+"正在考试.....");
}
}
1
2
3
4
5
6
7
子类:大学生
public class graduate extends student{
//在这里就可以写大学生自己的方法 such as
public void testing(){
System.out.println("小学生"+name+"正在考试.....");
}
}
1
2
3
4
5
6
7
8
9
10
使用类
public class use{
public static void main(String[] args) {
pupil p = new pupil();
p.name="李华";
p.testing();
p.setScore(60);
p.showinfo();
}
}

继承细则

1
2
子类继承了所有的属性和方法,但是私有属性和方法不能在子类中直接访问,
要通过父类提供公共的方法去访问
1
2
3
4
5
6
7
8
子类必须调用父类的构造器,完成父类的初始化
实际是编译器会在子类的构造器中添加super()来
调用父类的无参构造器
Example:
public class sub extends base{
super();//默认调用父类的无参构造器
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器,如果父类没有
无参构造器,则必须在子类的构造器中使用super去指定使用父类的哪个构造器完成对父类的初始化工作
Example:
父类无无参构造器,有有参构造器:
ps:当父类有了有参构造器后无参构造器就被覆盖了,就没有无参构造器了
public class base(){
public String name;
public int age;
public double score;

public base(String name,int age){

}
}
这时必须要在子类的构造器中使用super去指定使用父类的构造器,来完成对父类的初始化
public class sub extends base(){
public sub(){
super("李华",22)//调用父类的有参构造器完成对父类的初始化
}
}

1
super()在使用时需要放在构造器中的第一行
1
如果希望调用子类的指定构造器,显示的在子类中调用super()申明调用的是哪一个
1
super()和this()都只能放在构造器中的第一行,因此不能在构造器中共存;
1
2
java所有类都是Object类的子类
父类构造器的调用不限于直接父类,将一直往上追溯到Object类(低级父类)

alt text

1
2
java是单继承机制,子类最多只能继承一个父类
若想让A同时继承B,C类,可以让A继承B,B继承C来实现

super()

super()代表父类的引用,用于访问父类的属性,方法,构造器

  • 访问父类的属性,但不能访问父类的私有属性。super.属性名
  • 访问父类的方法,但不能访问父类的私有方法。super().方法名()
  • 访问父类的构造器

super()细则

  • 调用父类构造器的好处,父类的属性由父类初始化,子类的属性由子类初始化
  • 当子类中有和父类重名的属性、方法时,为了访问父类的必须通过super。如果没有重名则super、this、直接返回的效果一致
  • super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员
  • 如果多个基类(上级类)中都有同名的成员,使用super访问时遵循就近原则

方法重写override

1
如果子类有方法和父类的方法:名称、返回类型、参数都一致,就说子类的方法重写了父类的方法

多态

它允许同一个方法或接口在不同对象上具有不同的实现方式,Java作为一种面向对象的编程语言,广泛地利用了多态特性来提高代码的灵活性和可扩展性。

多态的前提是:两个对象(类) 存在继承关系

方法的多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com;

public class use {
public static void main(String[] args) {
A a =new A();
System.out.println(a.sum(10,20));
System.out.println(a.sum(10,20,30));

B b = new B();
a.say();
b.say();
}
}
class B{
public void say(){
System.out.println("B say()方法被调用");
}
}

class A extends B{
public int sum(int a,int b){
return a+b;
}
public int sum(int a,int b,int c){
return a+b+c;
}
public void say(){
System.out.println("A say()方法被调用");
}
}

重载: 这里传入不同的参数就调用不同的sum()方法,体现了多态
重写: 父类和子类中都有say()方法,但是对象不同,因此会根据对象去调用不同的方法,体现多态

对象的多态

一个对象的编译类型和运行类型可以不一致
编译类型在定义对象时,就确定了,不能改变
运行类型是可以变化的
编译类型看定义时 ‘=’ 的左边,运行类型看 ‘=’ 号的右边

Simple

1
2
3
4
5
6
7
package com;

public class Animal {
public void cry(){
System.out.println("Animal() 动物在叫唤");
}
}
1
2
3
4
5
6
7
8
package com;

public class Dog extends Animal{
public void cry(){
System.out.println("Dog() 小狗汪汪叫");
}
}

1
2
3
4
5
6
7
8
package com;

public class Cat extends Animal{
public void cry(){
System.out.println("Cat() 小猫喵喵叫");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
package com;

public class use {
public static void main(String[] args) {
//animal的编译类型是Animal,运行类型是Dog
Animal animal = new Dog();
animal.cry();

//animal的编译类型是Animal,运行类型是Cat
Animal animal = new Cat();
animal.cry();
}
}

Summary:一个父类的对象引用可以指向它的子类的对象,在运行时是以运行类型为主的

Simple:使用多态实现主人给动物喂食

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com;

public class Master {
private String name;
public Master(String name){
this.name=name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public void feed(Animal animal,Food food){
System.out.println("主人: "+this.name+"给"+animal.getName()+"喂"+food.getName());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com;

public class Animal {
private String name;

public Animal(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
8
package com;

public class Dog extends Animal{

public Dog(String name) {
super(name);
}
}
1
2
3
4
5
6
7
8
package com;

public class Cat extends Animal{

public Cat(String name) {
super(name);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com;

public class Food {
private String name;
public Food(String name){
this.name=name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
package com;

public class Bone extends Food{
public Bone(String name){
super(name);
}
}
1
2
3
4
5
6
7
8
9
package com;

public class Fish extends Food{

public Fish(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

package com;

public class use {
public static void main(String[] args) {
Master master = new Master("李华");

Animal animal = new Dog("旺财");
Food food =new Bone("大棒骨");

Animal animal1 = new Cat("咪咪");
Food food1 = new Fish("小黄鱼");

master.feed(animal,food);
master.feed(animal1,food1);
}
}

向上转型

本质: 父类的引用指向了子类的对象
特点:

1:可以调用父类中的所有成员(须遵守访问权限)
2:不能调用子类中特有成员和方法
3:最终运行效果看子类的具体实现

Simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com;

public class Animal {
String name="动物";
int age=10;

public void run(){
System.out.println("跑");
}

public void sleep(){
System.out.println("睡");
}

public void eat(){
System.out.println("吃");
}

public void show(){
System.out.println("你好");
}
}

1
2
3
4
5
6
7
8
9
10
package com;

public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void CatchMouse(){
System.out.println("猫抓老鼠");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com;

public class use {
public static void main(String[] args) {
//左边是编译类型,右边是运行类型
Animal animal = new Cat();
//Object也是Cat的父类
Object obj = new Cat();

//animal.CatchMouse();错误
//原因:在编译阶段能调用哪些方法由编译类型决定

//最终运行结果要看子类(运行类型)的具体实现,即调用方法时先找子类,子类没有招父类
//子类有eat方法,用子类的
animal.eat();
//子类没有,父类有,就调用父类的
animal.run();
animal.show();
animal.sleep();
}
}

向下转型

语法: 子类类型 引用名 = (子类类型) 父类引用;

Detail:

  • 只能强转父类的引用,不能强转父类的对象
  • 要求父类的引用必须指向的是当前目标类型的对象
  • 当向下转型时可以使用子类类型中的所有成员

Simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com;

public class Animal {
String name="动物";
int age=10;

public void run(){
System.out.println("跑");
}

public void sleep(){
System.out.println("睡");
}

public void eat(){
System.out.println("吃");
}

public void show(){
System.out.println("你好");
}
}

1
2
3
4
5
6
7
8
9
10
package com;

public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void CatchMouse(){
System.out.println("猫抓老鼠");
}
}
1
2
3
4
5
6
7
package com;

public class Dog extends Animal{
public void WatchDoor(){
System.out.println("小狗看门");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com;

public class use1 {
public static void main(String[] args) {
//animal是父类的引用
Animal animal = new Cat();
//向下转型
//子类类型 引用名 = (子类类型) 父类引用
//Cat cat = (Cat) animal

//animal是父类的引用,它指向的是子类(Cat)的对象
Cat cat = (Cat) animal;

//向下转型后可以调用子类类型中的成员
cat.CatchMouse();

//animal1指向的是子类(Dog)的对象
//因此animal1不能向下转型为Cat
//Animal animal1 = new Dog();
//Cat cat1 =(Cat) animal1;
}
}

属性重写问题

属性没有重写一说,属性的值看编译类型

Simple:instanceof 用于判断某个变量的运行类型为xx类型或xx类型的子类型

1
2
3
4
5
6
7
package com;

public class Base {
int a=20;

}

1
2
3
4
5
6
package com;

public class Sub extends Base{
int a=10;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com;

public class use {
public static void main(String[] args) {
Sub sub = new Sub();

//sub是Sub类型
System.out.println(sub instanceof Sub);//true
//sub是Base的子类型
System.out.println(sub instanceof Base);//true

//向上转型
//base的编译类型是Base,运行类型是Sub
Base base =new Sub();
System.out.println(base instanceof Base);//true
System.out.println(base instanceof Sub);//true
//从上述结果可以知道 instanceof 比较的是运行类型
//若比较的是编译类型则 base instanceof Sub 不会是 true
}
}

动态绑定机制

当调用对象方法 的时候,该方法会和该对象的内存地址/运行类型 绑定
当调用对象属性 时,没有动态绑定机制哪里声明,哪里使用

Simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com;

public class Base {
public int i = 10;
public int sum(){
return geti()+10;
}
public int sum1(){
return i+10;
}
public int geti(){
return i;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com;

public class Sub extends Base{
public int i=20;
// public int sum(){
// return i+20;
// }
public int geti(){
return i;
}
public int sum1(){
return i+10;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com;

public class use {
public static void main(String[] args) {
//向上转型:base编译类型是Base,运行类型是Sub
Base base = new Sub();

//当把Sub的sum方法注释掉时:
//由于base的运行类型是Sub,因此会先从Sub中找sum,Sub中没sum
//因此会从父类中找sum,父类中找到了sum
//但是此时父类中的sum调用geti(),父类,子类中都有,此时就要有动态绑定机制了
//base是Sub运行类型,因此会调用子类中的geti()
System.out.println(base.sum());//30
}
}

==操作符

  • 既可以判断基本数据类型,也可以判断引用数据类型
  • 判断基本数据类型时判断值是否相等
  • 判断引用数据类型时则判断地址是否相等,即判断是否是同一个对象

equals()方法

  • equals()方法是Object类中的一个方法,只能判断引用类型
  • 默认判断的是地址是否相同,但是子类中往往会重写此方法,用于判断内容是否相同
    • Integer
    • String

SourceCode

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}
1
2
3
4
5
6
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

hashcode

返回对象的哈希码值

总结:

  • Object类中的一个方法
  • 提高具有哈希结构的容器的效率
  • 两个引用,如果指向同一个对象,则哈希值一定是一样的
  • 两个引用,如果指向的是不同对象,则哈希值是不一样的
  • 哈希值主要根据内部地址转换得来,不能完全将哈希值等价于地址

Simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com;

public class test {
public static void main(String[] args) {
AA aa = new AA();
AA aa1 = aa;
AA aa2 = new AA();
System.out.println(aa.hashCode());//22307196
System.out.println(aa1.hashCode());//22307196
System.out.println(aa2.hashCode());//10568834

}
}
class AA{}

toString

SourceCode:返回全类名(包名+类名)+”@”+hashcode的16进制

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • Object类中的一个方法
  • 当输出一个对象时默认调用toString方法

类变量/静态变量

类变量也叫静态变量,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的值都是相同的,任何一个该类的对象去修改它时,修改的也是同一个变量。

语法:
访问修饰符 static 数据类型 变量名;
public static int count = 0;

访问类变量:
类名.静态变量(推荐)
对象名.静态变量

注意:类变量随着类的加载而创建,所以即使没有创建对象实例也可以访问

Simple

1
2
3
4
5
6
7
8
9
10
11
12
13
package com;

public class test {
public static void main(String[] args) {
System.out.println(AA.name);
AA aa = new AA();
System.out.println(aa.name);

}
}
class AA{
public static String name = "你好";
}

类方法/静态方法

静态方法是属于类而不是类的实例的方法。它可以在不创建类的实例的情况下被调用。
静态方法通常用于执行与类相关的操作,而不需要访问或修改特定实例的状态。

1
2
3
4
5
6
7
8
9
//语法:
//[访问修饰符] [static] [返回类型] [方法变量名(){}];
public static int getNum(){
return num;
}

//访问类方法;
类名.方法名
对象名.方法名

内部引用:在静态方法中不能使用this关键字,因为它没有当前对象的引用。实例方法可以使用this来引用当前对象

生命周期:静态方法在类加载时初始化,实例方法在对象创建时初始化

Simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com;

public class Student {
public String name;
public static double fee=0;

public Student(String name){
this.name=name;
}

public static void payFee(Double fee){
Student.fee += fee;
}

public static void showFee(){
System.out.println(Student.fee);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com;

public class use {
public static void main(String[] args){


Student lihua = new Student("李华");
//lihua.payFee(145.3);
Student.payFee(145.3);//这两种方法都可以

Student zhangsan = new Student("张三");
//zhangsan.payFee(142.3);
Student.payFee(142.3);

System.out.println(Student.fee);

}
}

代码块

代码化块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{ }包围起来。但其和方法不同,没有方法名、返回值、参数,只有方法体,且不用通过对象或类显式调用,而是在加载类时或创建对象时被隐式调用。

1
2
3
4
5
//语法
[修饰符]{
代码
};

修饰符可写可不写,写的话只能写static

有static的叫静态代码块,没static的叫普通代码块

逻辑语句可以为任何逻辑语句。(输入、输出、循环、方法调用、判断等)

最后的 ‘;’ 可写可不写

有static修饰(静态代码块):作用是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。

无static修饰(普通代码块):每创建一个对象都会执行一次

普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次就会调用一次。

如果只是使用类的静态成员(静态方法、静态属性)时,普通代码并不会执行

普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次就会调用一次。

如果只是使用类的静态成员(静态方法、静态属性)时,普通代码并不会执行

静态代码块只能调用静态方法、静态属性

普通代码块可以调用任意成员

调用顺序

1:先是静态属性、静态代码块的调用。它们两个的优先级一样,如果它们同时存在,则按照顺序进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com;

public class block {
public static void main(String[] args) {
A a = new A();
//静态属性被调用
//静态代码块被调用
}
}
class A{
//静态属性
public static int num=getNum();
//静态代码块
static{
System.out.println("静态代码块被调用");
}
public static int getNum(){
System.out.println("静态属性被调用");
return 10;
}
}

2:在调用普通代码块和普通属性的初始化,它们两个的优先级一样,如果同时存在,则按顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

package com;

public class block {
public static void main(String[] args) {
A a = new A();
//静态属性被调用
//静态代码块被调用
//普通属性初始化
//普通代码块被调用
}
}
class A{
//静态属性
public static int num=getNum();
//普通属性
public String name = getName();
//静态代码块
static{
System.out.println("静态代码块被调用");
}
//普通代码块
{
System.out.println("普通代码块被调用");
}
public static int getNum(){
System.out.println("静态属性被调用");
return 10;
}

public String getName(){
System.out.println("普通属性初始化");
return "hello";
}
}

3:最后调用构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com;

public class block {
public static void main(String[] args) {
A a = new A();
//静态属性被调用
//静态代码块被调用
//普通属性初始化
//普通代码块被调用
//构造器被调用
}
}
class A{
//静态属性
public static int num=getNum();
//普通属性
public String name = getName();
//构造器
public A(){
System.out.println("构造器被调用");
}
//静态代码块
static{
System.out.println("静态代码块被调用");
}
//普通代码块
{
System.out.println("普通代码块被调用");
}
public static int getNum(){
System.out.println("静态属性被调用");
return 10;
}

public String getName(){
System.out.println("普通属性初始化");
return "hello";
}
}

构造器的隐含

构造器的前面隐含了:super()和调用本类中的普通代码块

Simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com;

public class block {
public static void main(String[] args) {
B b = new B();
//A的构造器被调用
//B类的普通代码块被调用
//B的构造器被调用
}
}
class A{
public A() {
//super()
//调用本类中的普通代码块
System.out.println("A的构造器被调用");
}
}

class B extends A{
public B() {
//super()
//调用本类中的普通代码块
System.out.println("B的构造器被调用");
}
{
System.out.println("B类的普通代码块被调用");
}
}

在这个例子中实例化了B类对象,因此会调用B类的构造器,B类构造器中隐含了super()、调用本类无参代码块,因此实际会先调用B类的父类A的构造器,A的构造器也隐含,因此会先调用A的父类Object的构造器

[warning]