4

【Java】入门笔记2

 3 years ago
source link: https://www.guofei.site/2016/06/12/java2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【Java】入门笔记2

2016年06月12日

Author: Guofei

文章归类: 语言,文章编号: 12002


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2016/06/12/java2.html

Edit

方法

public/protected/private stastic void/int methodName(int inp){
  return inp
}

访问修饰符 本类 同包 跨包子类 其它 private 可       默认(不加修饰符) 可 可     protected 可 可 可   public 可 可 可 可

public class HelloWorld {
    // 1. 无参数无返回值方法
    public void printStar() {
        System.out.println("*********");
    }
    // 2. 有参数有返回值方法
    public float myFunc(float a,float b){
        printStar(); // 3. 不在主方法中调用方法,就可以不用 new 一个
        return a+b;
    }

    public static void main(String[] args) {
        // 4. 在main中调用方法:
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.printStar();
        float c= helloWorld.myFunc(1.2f,1.5f);
        System.out.println(c);
        helloWorld.printStar();
    }
}

重载

方法的重载:

  • 方法名相同,但入参列表不同。
  • 入参列表不同指的是入参的类型不同(顺序、个数、类型)。如果按顺序看类型和个数相同但参数名不同,是不能重载的。例如 func(String a,int b)func(String c, int d) 认为是入参相同。
  • 返回值可以不同,访问修饰符可以不同。
// 一个重载的例子
public class D {
    public void func(int a) {
        System.out.println("int");
    }

    public void func(String a) {
        System.out.println("String");
    }

    public static void main(String[] args) {
        D d = new D();
        d.func(1);
        d.func("1");
    }
}

可变参数类型

可变参数列表

public class HelloWorld {
    public int get_sum(int... n) { // 可变参数列表
        int sum = 0;
        for (int i : n) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) {
        HelloWorld helloWord = new HelloWorld();
        System.out.println(helloWord.get_sum(1));
        System.out.println(helloWord.get_sum(1, 2, 3));
    }
}

可变参数类型2

public class HelloWorld {
    public boolean isIn(int a, int... n) { // 可变参数必须放到最后。这个n可以传入数组,因此不能再写一个同名方法重载另一个n是数组的方法了。
        for (int i : n) {
            if (a == i) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        HelloWorld helloWord = new HelloWorld();
        System.out.println(helloWord.isIn(1, 1, 2, 3, 4));
        System.out.println(helloWord.isIn(5, 1, 2, 3));
    }
}

可变参数列表:

  • 可变参数必须放到最后。
  • 可变参数位置的可以传入数组。因此不能再写一个同名方法重载另一个n是数组的方法了(因为已经有了这样一个方法了)。
  • 多个同名方法同时满足时,可变参数最后被调用。例如,两个方法 func(int a, int b), func(int... a),那么调用 func(1,2) 会优先调用第一个。

方法的传值问题:在方法中改变一个变量的值,方法外会怎样?

  • (与 Python 一样)
  • 如果传入int等,方法中改变值后,方法外不变
  • 如果传入数组,方法外会跟着变
  • 如果传入自定义对象,方法外会跟着变

初始化

// 下面是一个声明+初始化对象的语言。
Cat one = new Cat();

以上语句完成了下面3给步骤

  1. 声明一个对象:在栈里面开辟一个空间,内容为 null
  2. 初始化一个对象:在堆里面开辟一个空间,初始化一个对象。
  3. 通过赋值操作,把栈和堆关联起来:堆空间的内存号存入到栈空间。

构造方法

  • 构造方法的方法名必须与类名相同
  • 构造方法没有返回值
  • 构造方法可以有参数或无参数,因此可以重载。
  • 构造方法只能在实例化时被调用。不能在其它地方被调用,不能被普通方法调用。
  • 如果没有显式指定构造方法,会自动指定一个无入参的构造方法
  • 构造方法可以用相互调用,用 this(args)
  • (不推荐)可以写一个同名的普通方法,不会有语法错误,但不推荐这么干。

package com.guofei.learnjava;

public class Cat {
    // 属性:
    String name;
    int month;
    double weight;
    String species = "中华田园猫";

    public Cat() { // 无参数的构造方法
        System.out.println("无参构造一个猫"); // 构造方法不能有返回值
    }

    // 构造方法可以多态
    public Cat(String name) { // 有参数的构造方法
        this(); // 构造方法可以相互调用,必须放第一行
        System.out.println("有参构造一个猫");
        this.name = name; // (没啥用:如果不加this,会自动寻找最近的name赋值,就不是属性那个name,改名也可以解决此问题)
        this.speak(); // 构造方法可以引用普通方法。这里 this 可以省略
    }

    //方法
    public void speak() {
        System.out.println(name + ": 喵喵喵");
    }
}

使用:(下面两个会执行不同的构造方法)

Cat one = new Cat();
Cat two = new Cat("花花");

封装

隐藏某些信息,以接口的方式提供给外界使用。

  • 属性设为 private
  • 创建 getter/setter 方法,方法名如 setName
  • 在 getter/setter 中做属性访问控制
  • 如果构造函数中给隐藏的属性赋值了,建议调用set方法赋值,以复用变量检查的代码。
  • getter/setter 可以分别省略,以达到只读/只写效果

public class Cat {
    private String name;
    private int month;
    public void setMonth(int month) {
        if (month <= 0) {
            System.out.println("年龄不能为负");
        } else {
            this.month = month;
        }
    }
    public int getMonth() {
        return this.month;
    }
}

static

static 归属于类。

  • 静态属性。在一个对象中改了静态属性后,所有对象以及类的属性都会变。(这与Python有些区别,Python的静态属性在赋值后不按照静态属性的逻辑来)
  • 静态方法。更推荐用类来调用静态方法,而不是用对象来调用。
  • 方法内不能定义 static 属性,只能定义 finnal 属性。
  • 方法可以对 static 做赋值/读取。
  • 静态方法不能调用动态属性,也不能调用动态方法。(因为动态属性和动态方法是属于对象的)
  • 解决方案:可以实例化后调用。

动态代码块和静态代码块


public class Cat {
    public Cat() {
        System.out.println("宠物猫来了!");
    }

    {
        System.out.println("构造代码块,在构造的时候会被执行");
    }

    static {
        System.out.println("静态代码块,比动态代码块先执行,且只会被调用一次");
    }

}

// 调用:
Cat cat1=new Cat();
Cat cat2=new Cat();

静态代码块,比动态代码块先执行,且只会被调用一次 构造代码块,在构造的时候会被执行 宠物猫来了! 构造代码块,在构造的时候会被执行 宠物猫来了!

  • 普通代码块:在方法中的代码块,在代码块中定义的变量,作用范围仅限于当前代码块。
  • 类代码块在构造前执行,包括静态代码块、动态代码块。
  • 静态代码块先执行(与位置无关)
  • 动态代码块每次构造都被执行,静态代码块只会执行一次。
  • 与方法一样,静态代码块只能调用静态属性和静态方法。

继承

public class Cat extends Animal{}
  • 只能继承一个类

重写 (Override):子类可以写一个同名的方法,覆盖掉父类的同名方法。

  • 方法名、返回值类型必须相同
  • 参数列表与被重写方法的参数列表必须完全相同。如果不同,其实是重载了。
  • 权限 子类方法的访问权限必须大于或等于父类方法的访问权限。(修饰符和权限的关系见于上表)
  • final 的方法不能被重写。
  • static 的方法不能被重写,但是能够被再次声明。
  • 构造方法:子类构造方法必须调用父类构造方法。如果不显式声明如何调用父类构造方法,会默认调用父类的无参构造方法。如果显式声明,会按照显式声明来。(见代码)。显式声明必须放到第一行。
  • this()super() 都必须在第一行,因此在不能同时出现在同一个构造方法中
  • 如果不能继承一个方法,则不能重写这个方法。
  • 执行顺序: 父类静态属性 -> 父类静态代码块 -> 子类静态代码块 -> 父类构造代码块->父类构造方法 -> 子类构造代码块 -> 子类构造方法
// Animal.java
public class Animal {
    public String name;

    public Animal() {
        System.out.println("构造一个 Animal");
    }

    public Animal(String name) {
        System.out.println("构造了一个 Animal:" + name);
        this.name = name;
    }

    public void eat() {
        System.out.println(this.name + " 最近没有食欲!");
    }

    public void eat(String food) {
        System.out.println(this.name + " 最近没有食欲!");
    }
}




// Dog.java
public class Dog extends Animal {//只能继承一个类

    public Dog() {
        System.out.println("构造一个 Dog");
    }

    public Dog(String name) {
        // 子类构造方法必须调用父类构造方法。
        //  super(); // 如果不显式声明会默认调用父类的无参构造方法。
        super(name); // 如果显式声明,会按照显式声明来。
        // 显式声明必须放到第一行。
    }

    public void eat(String food) { // 方法名、返回值类型必须相同。
        super.eat(); // 可以用 super 调用 父类的 eat()
        System.out.println("dog" + this.name + " 吃" + food);
    }

    public int eat(String food, int volume) { // 这个其实不是重写,而是重载
        this.eat(food);
        return volume - 1;
    }
}
Dog dog1 = new Dog();
dog1.eat(); // Dog 类并没有重写此种参数的 eat(),因此调用的是父类
System.out.println("=========");
Dog dog2 = new Dog("点点");
dog2.eat("饼干");
int left = dog2.eat("牛奶", 10);
System.out.println("牛奶剩下"+left);

构造一个 Animal 构造一个 Dog null 最近没有食欲! ========= 构造了一个 Animal:点点 点点 最近没有食欲! dog点点 吃饼干 点点 最近没有食欲! dog点点 吃牛奶 9

object类

object 类是所有类的父类。在 java.lang 这个包里面。

  • equals 默认是判断内存指向是否一致。可以重写,例如 String 类型的 equals 就重写为判断内容是否相同(但是字符串的 == 还是比较的是内存指向)
// 重写 equals的案例
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    Animal tmp = (Animal) obj;
    return this.name.equals(tmp.name);
}
// 还可以这样重写
public boolean equals(Animal obj) {
    if (obj == null) {
        return false;
    }
    Animal tmp = (Animal) obj;
    return this.name.equals(tmp.name);
}
  • toString(),对应 print(dog2), print(dog2.toString()),默认打印一个类似 com.guofei.work2.Dog@a09ee92 这样的东西。
    public String toString() {
      return "Animal{" +
              "name='" + name + '\'' +
              '}';
    }
    
  • hashCode() 对象的哈希代码值
  • getClass() 返回对象所属的类

final

package com.guofei.work2;

public final class Cat {// final class:不允许被继承
    final String name; //只能:1. 定义时赋值 2. 在构造函数中赋值,3. 构造代码块中被赋值。
    final static String author="guofei9987"; //final static 连用,可以防止被随意篡改。

    public Cat(){
        this.name="name"; // final 一个属性后,必须赋值。
    }

    public final void eat(){ // final 方法:不允许子类被重写,但可以被子类使用。
        final int a=1; //final 修饰局部变量:第一次赋值后不允许再更改。
        System.out.println("");
    }
}
  • 可以修饰类,表示类不能被继承
  • 可以修饰方法,表示方法不能被重写。但是可以被子类继承和使用。final不能修饰构造方法。
  • 可以修饰类的成员变量,只有3种赋值方法: 1. 定义时赋值 2. 在构造函数中赋值,3. 构造代码块中被赋值。赋值后不允许更改。
  • 可以修饰局部变量,第一次赋值后不允许再更改。

命名规则:

  • 按照域名倒叙

包文件管理,例如,我们创建了一个 Cat 类,然后想要另创建一个机器猫也叫 Cat. 这就需要另建一个包。

建立两个package,分别是如下内容:

// com.guofei.animal
package com.guofei.animal;

public class Cat {
    public Cat(){
        System.out.println("宠物猫来了!");
    }
}

// com.guofei.robot
package com.guofei.robot;

public class Cat {
    public Cat(){
        System.out.println("机器猫来了!");
    }
}

调包有以下几种:

import com.guofei.robot.Cat; // 方法1:加载指定类
import com.guofei.animal.*; // 方法2:加载所有类,会被精确指定的(上一条语句)覆盖,这个覆盖与 import 的顺序无关

// 方法3:使用时指定:
com.guofei.animal.Cat cat2=new com.guofei.animal.Cat();

// 一个坑:import 只能找到指定目录下的类,而找不到子文件夹的类。

多态

//  向上转型
Animal one = new Animal();
Animal two = new Cat();
Animal three = new Dog();

one.eat();
two.eat();
three.eat();

//  two.run();  // 报错!因为是 Animal 类型,需要向下转型:
Cat four = (Cat) two;
four.run();

System.out.println(two.getClass()); // Cat
System.out.println(two instanceof Cat); // true
System.out.println(two instanceof Animal); // true

多态的2个例子:


public class Master {
    // 多态例子1,主人喂宠物后,又根据宠物类型不同 做另一个动作。
    public void feed(Animal obj) {
        obj.eat();
        if (obj instanceof Dog) {
            Dog tmp = (Dog) obj;
            tmp.sleep();
        }
    }

    // 多态例子2,主人根据自己时间养不同的宠物。
    public Animal pet;

    public Animal adoptPet(boolean hasTime) {
        if (hasTime) {
            pet = new Dog();
        } else {
            pet = new Cat();
        }
        return pet;
    }
}
public abstract class Animal{ // 如此,就不能 new Animal 了。
  public abstract void play(); // 抽象方法。
  // 1. 子类必须重写,除非子类也是抽象类。
  // 2. 所在类必须是抽象类。  

}

设计模式

单例模式

  • 创建对象占用资源较多。
  • 对某个资源要求统一读写,如配置信息、打印机
  • 多个实例会引起逻辑错误,如primary_key 生成器。
public class Printer {
    // 1. 私有构造
    private Printer() {
    }

    // 2. 私有静态实例
    private static Printer instance = new Printer();

    // 3. 提供一个接口,返回静态实例对象
    public static Printer getInstance() {
        return instance;
    }

}

// 测试:
Printer one=Printer.getInstance();
Printer two=Printer.getInstance();
System.out.println(one==two);

懒汉式:(使用时才会创建)

public class Printer {
    // 1. 私有构造
    private Printer() {
    }

    // 2. 私有静态实例
    private static Printer instance = null;

    // 3. 提供一个接口,返回静态实例对象
    public static Printer getInstance() {
        if (instance == null) {
            instance = new Printer();
        }
        return instance;
    }

}
  • 饿汉用空间换时间
  • 懒汉用时间换空间
  • 懒汉存在多线程风险。

接口

  • 前面说了,只能继承一个
  • 很多时候,没有继承关系的类,想让他们又联系。例如 猫和机器猫都能跑,我们想把这个跑的动作关联起来。
public class Animal {
}


public interface IRunner {
    // 每个动作消耗卡路里。
    int calorie = 20; // 属性,默认public static final
    public void run(); // 必须被重写
    default void jump(){ // 加上 default 后,就不必被重写了。
        System.out.println("跳起");
    }
    static void stop(){
        System.out.println("停下来");
    }
}



public interface IPlayer {
    public void run();
}


public class Cat extends Animal implements IRunner, IPlayer { // 多个 interface
    @Override
    public void run() {
        System.out.println("猫咪跑跑");
    }

    @Override
    public void jump() {
        System.out.println("猫咪会跳");
    }
}

public class RobotCat implements IRunner{
    @Override
    public void run() {
        System.out.println("机器猫用轮子跑");
    }

}

内部类

可以在类、方法中定义另一个类。

成员内部类

public class Person {
    int age;

    // 1. 成员内部类
    class Heart {
        public String beat() {
            return "心脏在跳动";
        }
    }

    public Heart getHeart() {
        return new Heart();
    }
}

// 获取:
Person.Heart myHeart;
// 获取方法1:
myHeart = new Person().new Heart();
// 获取方法2:
myHeart = p1.new Heart();
// 获取方法3:
myHeart = p1.getHeart();

内部类也可以添加修饰符 public/private 等。

内部类可以调用外部类的字段、方法:

public class Person {
    int age;

    public void eat() {
        System.out.println("人会吃东西");
    }

    class Heart {
        public void beat() {
            eat(); // 内部类可以使用外部类的方法
            System.out.println(age + "岁的心脏在跳动"); //内部类可以使用外部类的字段
            // Person.this.age // 如果有同名歧义,可以精确指定。
        }
    }
}


Person p1 = new Person();
p1.age = 10;
Person.Heart myHeart = p1.new Heart();
myHeart.beat();

静态内部类

public class Person {
    public void eat() {
        System.out.println("人会吃东西");
    }
    public static void run(){
        System.out.println("人会跑");
    }

    static class Heart {
        public void beat() {
            run(); // 静态内部类只能调用外部类的静态方法
            // eat(); // 报错,静态内部类不能调用外部类的非静态方法。
            new Person().eat(); // 作为解决,还可以 new 一个对象来调用
        }
    }
}

// 调用:
Person.Heart myHeart = new Person.Heart();

方法内部类

定义在方法内的类。

  • 作用范围在方法内
  • 方法内的成员,不能是 public、private、protected、static
  • 可以 final,abstract

匿名内部类

有点儿想匿名函数,使用时定义一个类。

  • 不能用 public、private、protected、static、abstract 修饰(这么修饰也没意义)
  • 不能用构造方法,可以用构造代码块
  • 不能有静态成员(也没有意义)
// Person.java
public abstract class Person {
    public abstract void eat() ;
}


// Test.java
public class Test {
    public void func(Person person) {
        person.eat();
    }

    public static void main(String[] args) {
        Test tst = new Test();
        tst.func(new Person() {

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



您的支持将鼓励我继续创作!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK