返回 登录
0

Java语言基础

学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:492139965 我们一起学Java!

一、OOP(Object-oriented Programming)面向对象程序编程

初谈类和对象,所谓万物皆对象,类和对象有什么区别和联系?

类,是对某一种类型的定义,比如字符串,动物,人,飞机等等,而对象是指具体的字符串,动物,人…

如:猪是类,定义了,猪,有体重,有年龄,可以吃饭,可以睡觉,而对象是这只猪,那只猪,或者某只猪起个名字,叫小白,那么这种具体的猪,就是对象,而小白这个名字,则称为对象的引用,因为通过小白,我们可以找到指定的那头猪。

package com.yq;

public class Pig {

    private int height;
    private int age;

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

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

    public String toString() {
        return getClass().getName() + "===@===" + Integer.toHexString(hashCode());
    }
//    public String toString() {
//        return getClass().getName() + "@" + Integer.toHexString(hashCode());
//    }

    public static void main(String[] args) {

        Pig xiaoBai = new Pig();
        Pig xiaoHei = new Pig();
        xiaoBai.eat();
        xiaoHei.sleep();
        System.out.println(xiaoBai);
        System.out.println(xiaoHei);
        System.out.println("xiaoBai==xiaoHei?" + (xiaoBai == xiaoHei));
        System.out.println(xiaoBai.getClass());
        System.out.println(xiaoHei.getClass());
        System.out.println("xiaoBai.getClass()==xiaoHei.getClass()?" + (xiaoBai.getClass() == xiaoHei.getClass()));
    }
}
eat()
sleep()
com.yq.Pig===@===74a14482
com.yq.Pig===@===1540e19d
xiaoBai==xiaoHei?false
class com.yq.Pig
class com.yq.Pig
xiaoBai.getClass()==xiaoHei.getClass()?true

关于 ‘==’

对于引用变量而言,比较的时候两个引用变量引用的是不是同一个对象,即比较的是两个引用中存储的对象地址是不是一样的。

对于基本数据类型而言,比较的就是两个数据是不是相等。

对于String类型,它是特殊而有趣的。

package com.yq;

public class Test {

    public static void main(String[] args) {
        //String作为一个对象来使用
        System.out.println("String作为一个对象来使用");
        String str = new String("hello");
        String str2 = "he" + new String("llo");
        System.out.println("str==str2?" + (str == str2));
        System.out.println(str + "@" + str.hashCode());
        System.out.println(str2 + "@" + str2.hashCode());
        //String作为一个基本类型来使用
        //如果String缓冲池内不存在与其指定值相同的String对象,那么此时虚拟机将为此创建新的String对象,并存放在String缓冲池内。
        //如果String缓冲池内存在与其指定值相同的String对象,那么此时虚拟机将不为此创建新的String对象,而直接返回已存在的String对象的引用。
        System.out.println("String作为一个基本类型来使用");
        String s1 = "java";
        String s2 = "java";
        System.out.println("s1==s2?" + (s1 == s2));
        System.out.println(s1 + "@" + s1.hashCode());
        System.out.println(s2 + "@" + s2.hashCode());
        System.out.println("String混合使用");
        String st1 = "hello java";
        String st2 = new String("hello java");
        System.out.println("st1==s2?" + (st1 == st2));
        System.out.println(st1 + "@" + st1.hashCode());
        System.out.println(st2 + "@" + st2.hashCode());
    }
}
String作为一个对象来使用
str==str2?false
hello@99162322
hello@99162322
String作为一个基本类型来使用
s1==s2?true
java@3254818
java@3254818
String混合使用
st1==s2?false
hello java@-1605094224
hello java@-1605094224

输出结果中,明明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;
    }

上述String源码中,可以看出,String类是使用它的 value值作为参数然后进行运算得出hashcode的,

换句话说, 只要值相同的String不管是不是一个对象,hash值全部相等。

在Object超类中,hashCode()是一个native本地方法,看不到实现。

对象存放在内存的堆中,而基本类型则存放在内存的栈中。

二、操作符

移位操作符<<,>>,>>>

移位操作符操作的对象是二进制的”位”,只能用来处理整数类型,如果对char、byte、或者short类型的数值进行移位处理,那么他们在移位之前会自动转换为int类型,结果也是int,对long处理,结果也是long。

<< : 左移运算符,低位补0,num << 1,相当于num乘以2

 :     右移运算符,若符号为正,高位补0,若符号为负,高位补1,num >> 1,相当于num除以2

: 无符号右移,忽略符号位,空位都以0补齐。

三元操作符 exp?value1:value2

若exp表达式为true,返回value1的值,否则返回value2的值,三元表达式可以嵌套。

‘+’操作符,即字符串操作符,用来连接字符串。

‘()type’类型转换操作符,用来强制类型转换,一般自动向上转型,不需要强转,向下转型才需要强转。

‘,’逗号操作符,java里面唯一用到逗号操作符的地方就是for循环的控制表达式,在表达式的初始化和步进控制部分,可以使用逗号分隔的语句,但是初始化时,使用逗号表达式,必须具有想同的类型。

三、流程控制

if-else、while、do-while、for、foreach、switch。

if语句不加{},默认范围为下一条语句。

else if不是关键字,只是else后面加了一个if语句。

do-while相比while只是会先执行一次,再判断条件。

for(;;)效果和while(true)一样。

foreach语法用于数组和容器。

package com.yq;

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        for (int i : arr)
            System.out.print(i + " ");
        System.out.println();
        List<String> ls = new ArrayList();
        ls.add("a");
        ls.add("b");
        ls.add("c");
        ls.add("d");
        ls.add("e");
        for (String str : ls)
            System.out.print(str + " ");
    }
}

//out
1 2 3 4 5 
a b c d e

关键字return、break、continue。

return结束整个方法,开始下一个方法。

break结束当前层的循环,开始当前层的外层的下一次循环。

continue结束当前层的当次循环,开始当前层的下一次循环。

switch的选择因子,是一个能够产生整数值的表达式,char本身就是整数。

四、类初始化

this关键字

1、表示当前类对象的引用

2、表示对调用当前方法的那个对象的引用

3、构造器调用类中的其他构造器,但是不能调用两次,并且必须放在构造器的第一行。

static关键字

static静态方法,可以通过类本身直接调用,当然类对象也可以调用static方法,但是static方法里面不能调用非静态方法,也不能出现this。

对于static修饰的类属性来说,内存只为该属性第一次初始化时分配一个地址,不管该类new了多少个对象,该属性都是同一个引用。

虽然地址是唯一的,即引用是唯一的,但是引用的值还是可以改变的。

final关键字

final作用于方式,则该方法不能被继承时覆盖,作用于参数,如果是引用类型,则该引用不能改变,但是引用的对象可以改变,如果是基本类型,则只能被赋值一次,并且不能改变。

对于一个类说来,该类的成员属性,可以不初始化,因为系统会为各成员属性设置默认值,String、对象等默认为null,数值类型为0,对应folat或者double,也有相应的小数点,char也为0,显示为空白,boolean为false,String、对象则为null。

只有类成员属性会被默认初始化,方法中必须对定义的属性或变量初始化。

初始化顺序

构造器不指定时,系统将会设置一个默认无参构造器,若指定了一个构造器,无论是否有参数,系统都不会自动设置默认构造器。

下面的例子,详细分析了,各种情况的初始化顺序:

package com.yq;

class Animal {
    static int level;
    int type;

    static {
        System.out.println("Animal static");
        level = 1;
        System.out.println("Animal level:" + level);
    }

    {
        System.out.println("Animal");
        type = 10;
    }

    Animal(int i) {
        System.out.println("Animal constructor");
    }
}

class Pig extends Animal {
    static int level;
    int height;
    Eye eye = new Eye(2);

    static {
        System.out.println("Pig static");
        level = 2;
        System.out.println("Pig level:" + level);
    }

    {
        System.out.println("Pig");
        height = 20;
        super.level = 3;
    }

    Pig(int i) {
        super(i);
        System.out.println("Pig constructor");
    }
}

class Eye {
    Eye(int i) {
        System.out.println(i + "只眼睛");
    }
}

public class Test {
    public static void main(String[] args) {
        //测试1
        Pig pg1 = new Pig(2);
        System.out.println(Animal.level);
        //测试2
        Pig pg2 = new Pig(2);
        Pig pg3 = new Pig(2);
        System.out.println(Animal.level);
        //测试3
        System.out.println(Pig.level);
    }
}
//测试1输出
Animal static
Animal level:1
Pig static
Pig level:2
Animal
Animal constructor
2只眼睛
Pig
Pig constructor
3
//测试2输出
Animal static
Animal level:1
Pig static
Pig level:2
Animal
Animal constructor
2只眼睛
Pig
Pig constructor
Animal
Animal constructor
2只眼睛
Pig
Pig constructor
3
//测试3输出
Animal static
Animal level:1
Pig static
Pig level:2
2

由测试1可知,初始化顺序为:父类静态成员、父类静态代码块、子类静态成员,子类静态代码块、父类成员,父类代码块,父类构造器、子类成员、子类代码块、子类构造器。

由测试2可知,静态域只初始化一次。

由测试3可知,只调用静态域时,只初始化静态域,和对象无关,静态域只由类决定。

若父类没有无参构造器,则必须用super显式调用父类构造器,不然会报错。

可变参数列表

package com.yq;

public class Pig {
    static void test(String... args) {
        for (String str : args)
            System.out.print(str + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        test("小明", "小红", "小军", "小华");
    }
}

//输出
小明 小红 小军 小华

其实可变参数列表,是编译器自动填充了数组。

五、访问控制权限

public、protected、包访问权限(没有关键字,默认权限)、private

对于类来说,如果一个java文件,存在public类,则java文件名必须和类型一致,而且这个文件只能存在一个public类,若整个文件都不存在public类,则java文件名和类名没有关系。

对于成员属性和方法来说,

public是最大权限,任何类都可以访问。

protected,继承访问权限,在该类的继承类中可以访问,同一个包下可以访问。

包访问权限,当没有指明权限时,默认是包访问权限,只有在同一个包下的类才能访问。

private是最小的权限修饰符,只能在本类中访问。

六、复用类。

组合:在新的类中放置任意对象,一般是private修饰该对象

继承:在新的类中隐式的放置父级对象。

组合技术通常用于想在新类中使用现有类的功能,而非他的接口。

继承技术通常用于保持现有类的形式,并可以添加新代码。

一般来说判断是否使用继承,问一问自己是否需要从新类像父类进行向上转型,如果需要则继承是必须的,否则考虑组合技术应该更合适。

七、多态

因为多态的存在,程序有了可扩展性。

package com.yq;

class Animal {
    public void eat(String args) {
        System.out.println("Animal eat():" + args);
    }
}

class Pig extends Animal {
    public void eat(String args) {
        System.out.println("Pig eat():" + args);
    }
}

class Cat extends Animal {
    public void eat(String args) {
        System.out.println("Cat eat():" + args);
    }
}

public class Test {

    public static void action(Animal anl) {
        anl.eat("苹果");
    }

    public static void main(String[] args) {
        Pig pg = new Pig();
        Cat ct = new Cat();
        action(pg);
        action(ct);
    }
}

//输出
Pig eat():苹果
Cat eat():苹果

如果程序中,需要新加动物种类,则只需定义这个类,继承Animal类即可,主程序不用做任何修改。

那么为什么编译器能这么智能的找到对应的子类对象,并且正确的调用方法呢?

原因就是:方法后期绑定,也叫作动态绑定。

意思是,在运行时根据对象的类型进行绑定,而不是在定义方法就进行前期绑定。

java中除了static和final方法(private方法也属于final方法,因为final是隐式申明的)之外,其他所有的方法都是后期绑定。

学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:492139965 我们一起学Java!

评论