该学习笔记是根据阿玮老师《黑马程序员Java零基础视频教程》总结出来的,非常感谢阿玮老师。
一. 面向对象简单介绍
- 类和对象
- 类是对象共同特征的描述;对象是真实存在的具体东西。在Java中,必须先设计类才能获取对象。
- 类的定义:
[修饰符] class 类名 [extends 父类名] [implements 接口名] {1.成员变量(代表属性,一般是名词);2.成员方法(代表行为,一般是动词);3.构造器;4.代码块;5.内部类;}
。例子:public class Student {// 类体}
- 获取类的对象格式:类名 对象名 = new 类名(); 。
- 用来描述一类事物的类,专业叫做Javabean类,在Javabean类中,是不写main方法的。而我们写main方法的类叫做测试类,我们可以在测试类中创建Javabean类的对象并进行赋值调用。标准的Javabean类需要满足以下几点:类名需要见名知意;成员变量使用private修饰;提供至少两个构造方法(无参数构造方法、带全部参数的构造方法);成员方法(提供每一个成员变量对应的setXXX和getXXX、如果还有其它行为,也需要写上)。
- 扩展说明
- 类名首字母建议大写,需要见名知意,驼峰命名;
- 一个Java文件中可以定义多个class类,但只能有一个类是public修饰,而且public修饰的类名必须成为代码的文件名;实际开发中建议还是一个文件定义一个class类。
- 成员变量的完整定义格式是:
[修饰符] 数据类型 变量的名称 = 初始化值
,一般无需指定初始化值,存在默认值。例子:private String name;
。 - 面向对象的三大特征包括
封装、继承和多态
。 - 构造方法也叫做构造器、构造函数。构造方法的作用是在创建对象的时候给成员变量进行初始化(赋值)的。我们不能手动的调用构造方法,而是在创建对象的时候由java虚拟机自动调用,并且每创建一次对象就会调用一次构造方法。构造方法的格式为:
public class Student{
修饰符 类名(参数){
/*
此处的参数可以不写,变成空参数构造方法,这里的方法名与类名相同,没有返回值类型,连void都没有。如果我们没有显示的写任何构造方法,
那么java虚拟机会自动生成一个无参数的构造方法。但是如果自己写了构造方法,java虚拟机就不再提供无参构造方法了。
*/
方法体;
}
}
二. String 类
- String类定义在java.lang包中(java.lang.String),而java.lang包是java的核心包,所以在使用的时候不需要导包。java程序中所有字符串(例如"abcdefg")都是String类的对象。String(字符串类型)它是引用数据类型。创建后的字符串不可变,它的值在创建后不能被更改,这是因为所有的字符串都会被视为对象。字符串的常见构造方式如下所示:
// 1.使用直接赋值的方式获取一个字符串对象(使用次数较多)
String s1 = "helloWorld";
System.out.println("s1:"+s1);
//2.使用new关键字的方式来获取一个字符串对象.下面是一个空参构造,可以获取一个空白的字符串对象(使用次数较少)
String s2 = new String();
System.out.println("s2为空:"+"a"+s2+"c");
//3.传递一个字符串,根据传递的字符串内容再创建一个新的字符串对象(使用次数较少)
String s3 = new String("abc");
System.out.println("s3:"+s3);
// 传递一个字符数组,根据字符数组的内容再创建一个新的字符串对象
char[] chArr = {'a','b','c','d'};
String s4 = new String(chArr);
System.out.println("s4:"+s4);
// 传递一个字节(byte)数组,根据字节数组的内容再创建一个新的字符串对象
byte[] byArr = {97,98,99,100,101};
String s5 = new String(byArr);
System.out.println("s5:"+s5);
/*
输出:
s1:helloWorld
s2为空:ac
s3:abc
s4:abcd
s5:abcde
*/
- 字符串的比较
- 字符串的比较:
"=="
、"equals方法"
、"equalsIgnoreCase方法--忽略字母的大小写"
。"=="
在进行比较时,对于基本数据类型比较的是数据值,对于引用数据类型比较的是地址值。"equals方法"
比较字符串对象中的内容(数据)是否相同;"equalsIgnoreCase方法"
比较字符串对象中的内容是否相同,但它忽略字母大小写。 "=="
与"equals方法"
的区别:"=="
比较操作符两端的操作数是否为同一个对象,也就是比较地址值是否相同;"equals方法"
比较两个对象的内容(值)是否一样。- String和new String()的区别,下面以
String str1 = "abc";
、String str2 = "abc";
、String str3 = new String("abc");
为例。
(1)直接定义字符串String str1 = "abc";
,首先会在字符串常量池中查找有无"abc"这个对象,如果没找到,则在字符串常量池中创建一个"abc"对象,然后以str1作为"abc"对象的引用,并在栈内存中为str1开辟一个内存空间;若在字符串常量池中找到了"abc"这个对象,同样也将str1作为"abc"对象的引用。String str2 = "abc";
显然此时字符串常量池中已经存在"abc"这个对象,那么同样也将str2作为"abc"这个对象的引用,并在栈内存中为str2开辟一个空间。那么此时str1和str2是同一个"abc"对象的引用(也就是str1和str2的地址值相同),因此操作str1 == str2返回结果为true。(可以把 引用 看成是 地址值)
(2)String str3 = new String("abc");
通过new String()创建一个字符串str3,首先在字符串常量池中查找是否存在"abc"对象,如果有的话直接使用,没有的话就再重新创建一个"abc"对象;然后在堆内存中创建一个"abc"对象的对象,以str3作为堆内存中对象的引用,此时str3对应的是字符串常量池外堆内存中的一个对象,因此操作str1 == str3返回的结果为false。
(3)str1和str2存储的是常量池中的地址;str3存储的是堆内存中的一个内存空间地址,而这个内存空间存放的是常量池中的地址。 - 代码:
- 字符串的比较:
int a = 10;
int b = 12;
System.out.println(a==b); //此时比较10和12是否相等
// 创建4个字符串对象
String s1 = new String("abc");
String s2 = "abc";
String s3 = "abc";
String s4 = "ABC";
Scanner scan = new Scanner(System.in);
String s5 = scan.next(); //abc,通过键盘录入的abc是new出来的
// "=="比较. 基本数据类型:比较的是数据值;引用数据类型:比较的是地址值.
System.out.println(s1==s2);
System.out.println(s2==s3);
// s.equals方法:比较字符串对象中的内容是否相同;s.equalsIgnoreCase方法:比较字符串对象中的内容是否相同,忽略字母大小写.
System.out.println(s1.equals(s2));
System.out.println(s1.equalsIgnoreCase(s4));
System.out.println(s2==s5);
/*
输出:
false
abc
false
true
true
true
false
*/
- StringBuilder
- StringBuilder可以看成是一个容器,创建之后里面内容是可变的,其作用是提高字符串的操作效率。使用StringBuilder的场景:字符串的拼接;字符串的反转。StringBuilder的append()方法与"+“相比,在进行字符串拼接时是高效率的(也就是说使用”+"进行字符串拼接时效率很低的)。
- StringBuilder分析:StringBuilder在刚开始创建的时候,底层会创建一个字节(byte)数组,默认容量为16。在添加字符串的时候如果容量不够的话,会自动扩容:老容量*2+2=34。如果添加字符串的长度比容量34还大,则以实际容量为准。总结一下:步骤1–默认创建一个长度为16的字节数组;步骤2–添加的内容长度小于16,直接存;步骤3–添加的内容大于16会自动扩容(原来容量*2+2);步骤4–如果扩容之后还不够,就以实际长度为准。代码如下:
/*
(1)StringBuilder的构造方法:StringBuilder():创建一个空白可变字符串对象,不含有任何内容;
StringBuilder(String str):根据字符串(str)的内容,来创建可变字符串对象.
(2)StringBuilder成员方法名:append()--添加任意类型的数据,并返回对象本身; reverse()--反转容器中的内容;length():返回长度;
toString():通过toString()就可以实现把StringBuilder对象转换为String对象.
*/
// 创建StringBuilder对象
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("abc");
System.out.println(sb1);
System.out.println(sb2);
// 添加元素
sb1.append("ABC");
System.out.println(sb1);
sb2.append(66.66).append('y'); // 等价于:sb2.append(12.3);sb2.append('x');
System.out.println(sb2);
// 反转元素
sb1.reverse();
System.out.println(sb1);
//把StringBuilder对象转换为String对象
String s = sb2.toString();
System.out.println(s);
/*
输出:
abc
ABC
abc66.66y
CBA
abc66.66y
*/
- StringJoiner
StringJoiner跟StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的。StringJoiner作用是提高字符串的操作效率,而且代码编写特别简洁,但是目前市场上很少有人使用。简单代码如下:
/*
(1)StringJoiner的构造方法:StringJoiner(间隔符号):创建一个StringJoiner对象,指定拼接时的间隔符号;
StringJoiner(间隔符号,开始符号,结束符号):创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号.
(2)StringJoiner的成员方法: add(添加的内容):只能添加字符串类型的数据,并返回对象本身; length():返回长度;
toString():将StringJoiner对象转为toString()对象,也就是返回一个字符串.
*/
// 创建StringJoiner对象
StringJoiner sj1 = new StringJoiner(",");
StringJoiner sj2 = new StringJoiner(", ","[","]");
//添加元素
sj1.add("abc").add("efg").add("hij");
sj2.add("abc").add("efg").add("hij");
System.out.println(sj1);
System.out.println(sj2);
// 转为字符串
String s = sj2.toString();
System.out.println(s);
/*
输出:
abc,efg,hij
[abc, efg, hij]
[abc, efg, hij]
*/
- 字符串相关的底层原理
- 字符串存储的内存原理:使用直接赋值方式创建字符串会重复使用字符串常量池中已经存在的字符串,这种方式会节约内存;而new出来的字符串不会重复使用已有的字符串,每new出一个字符串就会在堆内存中开辟一个新的内存空间。
- 字符串拼接的底层原理:没有变量的字符串拼接代码如下:
// 没有变量的字符串拼接:在拼接时会触发字符串的优化机制,在编译的时候就已经是最终的结果了.
String s1 = "a"+"b"+"c";
//在编译的时候会自动将"a"+"b"+"c"拼接为"abc",等价于直接赋值得到"abc":String s1 = "abc";
String s2 = "abc";
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
/*
输出:
abc
abc
true
*/
有变量的字符串拼接代码如下:
// 有变量的字符串拼接:(JDK1.8之前)
String s1 = "a";
// 步骤1:在栈内存中创建变量s1,并在字符串常量池中创建一个字符串"a",此时变量s1记录的是"a"的地址值;
String s2 = s1+"b";
// 步骤2:首先这里的字符串"b"会在字符串常量池中创建.此时有变量s1参与拼接,这时候会在堆内存中创建一个
// StringBuilder对象(new StringBuilder();),然后通过append方法将s1的内容和字符串"b"添加到StringBuilder对象中.最后
// 通过toString()方法把StringBuilder对象转换为String对象,并将String对象的地址值赋值给变量s2.
// (等价于:String s2 = new StringBuilder().append(s1).append("b").toString();)
String s3 = s2+"c";
// 步骤3:和步骤2的过程类似,只不过会创建一个新的StringBuilder对象.步骤1,2,3共创建两个StringBuilder()、两个String对象,
// 浪费空间.因此如果有很多字符串变量拼接,不要使用"+",因为在底层会创建多个对象,浪费时间和性能.
//(JDK1.8版本)有变量的字符串拼接:系统会预估字符串拼接之后的大小,把要拼接的内容都放在数组中,此时也是产生一个新的字符串.
String s4 = "abc";
System.out.println(s3);
System.out.println(s4);
System.out.println(s3==s4);
/*
输出:
abc
abc
false
*/
- 最后再啰嗦啰嗦
使用下图解释解释直接赋值创建字符串对象
和通过关键字new创建字符串对象
的区别:
三. java修饰符
-
java的修饰符用来定义类、方法或者变量,通常放在语句的最前端,主要分为两类:访问修饰符(权限修饰符)、非访问修饰符。访问修饰符分为:public、protected、default、private;非访问修饰符分为:static、final、abstract等等。
-
访问修饰符
使用访问修饰符(权限修饰符)来保护或者修饰类、成员变量、成员方法和构造方法。换句话说权限修饰符用来控制一个成员能够被访问的范围。权限修饰符的使用规则:在实际开发中,一般只用private和public。使用权限修饰符一般会遵守:成员变量私有(private)、成员方法公开(public),如果方法中的代码是抽取其它方法中共性代码,这个方法一般也私有。
- private:私有访问,在同一类内可见,其余的都不可见。private作用于变量、方法,但不能修饰类(外部类),可以修饰内部类;
- default(空着不写):默认访问,不写任何修饰符,在同一个包内可见,其余不可见。作用于类、接口、变量、方法;
- protected:保护型访问,在同一个包内可见,对不同包下的子类可见。protected作用于变量、方法,但不能修饰类(外部类),可以修饰内部类。解释一下不同包下的子类:假如我在demo1包中创建了一个父类Person,然后我又创建一个demo2包,并在demo2包中创建一个子类Student(继承父类Person),那么Student这个类就叫做不同包下的子类。
- public:共有访问,对所有的类都可见。public作用于类、接口、变量、方法。
-
稍微提一嘴
- 包就是文件夹,用来管理各种不同功能的java类,方便后期代码维护。其中包的命名规则:公司域名反写+包的作用,
需要全部英文小写,见名知意
。例如:黑马官方网址是 www.itheima.com,那么包名为“com.itheima.domain”,其中domain就是包的作用。包名+类名叫做全类名(全限定名)---com.itheima.domain.Student
。 - 使用其它类的规则:(1)使用同一个包下的类时,不需要导包(同一个包,不需要导包);(2)使用java.lang包中的类时,不需要导包;(3)其它情况都需要导包;(4)如果同时使用两个包中相同名字的类时,需要用全类名。
- 包就是文件夹,用来管理各种不同功能的java类,方便后期代码维护。其中包的命名规则:公司域名反写+包的作用,
-
非访问修饰符
非访问修饰符分为:static、final、abstract等等。但是下面只介绍static和final。
4.1 static- static表示静态,可以修饰成员变量和成员方法,但是static不能修饰普通类,只能修饰内部类。我们将static修饰的成员变量叫做静态变量,static修饰的成员方法叫做静态方法。其中static修饰的成员变量(静态变量)可以被该类所有的对象所共享,调用的方式有两种:类名调用(推荐,既然所有的对象都共享该静态变量,那么这个静态变量就不属于某个特定的对象,而属于这个类实例化的所有对象。我们如果用对象名去调用的话,也可以,但是不太合理,反正尽量用类名调用)、对象名调用。静态变量的特点:(1)被该类所有的对象共享;(2)不属于对象,属于类;(3)随着类的加载而加载,优先于对象存在。静态方法的特点:(1)多用在测试类和工具类中;(2)Javabean类中很少会使用;(3)调用方式:类名调用(推荐)、对象名调用。
静态的变量和方法随着类的加载而加载(和类有关),非静态的变量和方法跟对象有关。
- JavaBean类:用来描述一类事物的类。例如:Student、Teacher、Dog、Cat等等;测试类:用来检查其它类是否书写正确,带有main方法的类,是程序的入口;工具类:不是用来描述一类事物的,而是帮我们做一些事物的类。下面介绍书写工具类的规则:
(1)类名见名知意;(2)私有化构造方法;(3)方法都定义为静态的,也就是使用static修饰成员方法。 - (1)静态方法只能访问静态变量和静态方法;(2)非静态方法可以访问静态变量和静态方法,也可以访问非静态的成员变量和非静态的成员方法;(3)静态方法中没有this关键字。简而言之,静态方法中只能访问静态,非静态方法可以访问所有,静态方法中没有this关键字。
- 代码块:分为局部代码块、构造代码块和静态代码块,其中局部代码块和构造代码块不太重要,了解就行。静态代码块格式:
static{ }
;静态代码块特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
。静态代码块使用场景:在类加载的时候,做一些数据初始化的时候使用。静态代码块代码如下: - 重新认识main方法
- static表示静态,可以修饰成员变量和成员方法,但是static不能修饰普通类,只能修饰内部类。我们将static修饰的成员变量叫做静态变量,static修饰的成员方法叫做静态方法。其中static修饰的成员变量(静态变量)可以被该类所有的对象所共享,调用的方式有两种:类名调用(推荐,既然所有的对象都共享该静态变量,那么这个静态变量就不属于某个特定的对象,而属于这个类实例化的所有对象。我们如果用对象名去调用的话,也可以,但是不太合理,反正尽量用类名调用)、对象名调用。静态变量的特点:(1)被该类所有的对象共享;(2)不属于对象,属于类;(3)随着类的加载而加载,优先于对象存在。静态方法的特点:(1)多用在测试类和工具类中;(2)Javabean类中很少会使用;(3)调用方式:类名调用(推荐)、对象名调用。
public class HelloWorld {
public static void main(String[] args) {
// 1. public: 被JVM(Java 虚拟机)调用,访问权限足够大;
// 2. static: 被JVM调用,不需要创建对象,直接通过类名访问;(也就是说JVM使用HelloWorld这个类名去调用main方法)
// 因为main方法是静态的,所以测试类中其它方法也需要是静态的.
// 3. void: 被JVM调用,不需要给JVM返回值;
// main: 一个通用的名称,虽然不是关键字,但是被JVM识别;
// String[] args: 以前用于接收键盘录入数据的,现在没有用了; []:数组, String:数据类型, args:数组名字(字符串数组)
System.out.println("helloWorld");
}
}
- 4.2 final
使用关键字final修饰表示最终的、不能被改变的,final可以修饰方法、类、变量。
(1)final修饰方法时,表明该方法时最终方法,不能被重写(修改);
(2)final修饰类时,表明该类时最终类,不能被继承(没有子类);
(3)final修饰变量时,该变量就叫做常量,只能被赋值一次,不能修改。如果final修饰的变量是基本数据类型,那么变量存储的数据值不能发生改变;如果final修饰的变量是引用数据类型,那么变量的地址值不能发生改变,但对象内部的属性值可以改变。 - 4.3 static内存图