面向对象简介
类与对象
类与对象的基本概念
在面向对象中类和对象是最基本、最重要的组成单元,那么什么叫类呢?类实际上是表示一个客观世界中某类群体的一些基本特征抽象,属于抽象的概念集合。如汽车、轮船、书描述的都是某一类事物的公共特征。而对象呢?就是表示一个个具体的事物,例如:张三同学、王五汽车,这些都是可以使用的事物,就可以理解为对象,所以对象表示的是一个个独立的个体。
例如,在现实生活中,人就可以表示为一个类,因为人本是属于一种广义的概念,并不是一个具体个体描述。而某一个具体的人,如张三同学,就可以称为对象,可以通过各种信息完整地描述这个具体的人,如这个人的名字、年龄、性别等信息,这些信息在类中称为属性,同时人也会产生行为,如跑步、吃饭等,在类中就称为方法。
类与对象的基本定义
从之前的概念可以了解到,类是由属性和方法组成的。如何定义一个类:
如图定义了一个Book类,在这个类中定义了两个属性:书的名字(title)、书的价格(price),以及一个区的书籍完整信息的getInfo()方法。
对象内存分配与实例化对象
类本身属于引用类型,而引用类型与基本类型最大的不同在于需要内存的开辟及使用,所以关键字new的主要功能就是开辟内存空间。
- 栈内存(heap):保存基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
- 堆内存(stack):保存对象的真正数据即属性
下面通过一个例子来解释:
// An highlighted block
Book bk = new Book();
注:title=null 和 price =0.0 是string 和double类型的默认值
// An highlighted block
bk.title = "Java开发";
// An highlighted block
bk.price = 89.9;
下面以分步的方式实例化对象
提问:如果使用了没有实例化的对象会如何?
答:会出现“NullPointerException”(空指向异常)
// An highlighted block
public class Test {
String title;
double price;
public static void main(String[] args) {
Test bk = null;
bk.title = "Java开发";
bk.price = 89.9;
bk.getInfo();
}
}
执行结果: Exception in thread "main" java.lang.NullPointerException
引用数据的初步分析
首先引用传递的核心概念只有一点:一块堆内存空间可以同时被多个栈内存共同指向,则每一个栈内存都可以修改同一块堆内存空间的属性值。
深入观察引用传递
通过内存分析可以发现,在引用数据类型关系时,一块没有任何栈内存指向的堆内存空间将成为垃圾,所有的垃圾会不定期地被垃圾收集器(Garbage Collector)回收,回收后会被释放掉其所占用的空间。
封装性初步分析
观察没有封装的代码
// An highlighted block
class Book{
String title;
double price;
public void getInfo(){
System.out.println("图书名称:"+title+",价格: "+price);
}
}
public class Test{
public static void main(String[] args) {
Book book = new Book();
book.title = "Java开发";
book.price = -89.9;
book.getInfo();
}
}
执行结果:图书名称:Java开发,价格:-89.9
上述代码,可以直接去操作属性,这显然是不合理的。
就好比银行,储户不可能自己直接去操作金库,必须由银行工作人员按照标准才能进行金钱操作。对该程序而言,就可以使用private关键字进行封装,将类中的属性私有化。
在实际开发中,所有在类中定义的属性都要求使用private声明,如果属性需要被外部所使用,那么按照要求定义相应的setter、getter方法。
// An highlighted block
class Book{
private String title;
private double price;
public void setTitle(String t){
title = t;
}
public void setPrice(double p){
price = p;
}
public String getTitle(){
return title;
}
public double getPrice(){
return price;
}
public void getInfo(){
System.out.println("图书名称:"+title+",价格: "+price);
}
}
public class Test{
public static void main(String[] args) {
Book book = new Book();
book.setTitle("Java开发");
book.setPrice(-89.9);
book.getInfo();
}
}
构造方法
如果要实例化新的对象,需要使用关键字new来完成,但是除了new这个关键字之外,还有可能在对象实例化时为其进行一些初始化的准备操作,这个时候就需要构造方法的支持。构造方法本身是一种特殊的方法,它只在新对象实例化的时候调用,其定义原则是:方法名称与类名称相同,没有返回值类型声明,同时构造方法也可以进行重载。
构造方法一直存在
如果明确定义一个有参的构造方法,就不会再自动生成默认的构造方法,即一个类中至少会保留一个构造方法。
定义构造方法
构造方法为属性赋值
构造方法重载
匿名对象
按照之前的内存关系来讲,对象的名字可以解释为在栈内存中保存,而对象的数据(属性)在堆内存中保存,这样一来,没有栈内存指向堆内存空间,就是一个匿名对象
// An highlighted block
class Book{
private String title;
private double price;
public Book(String s ,double d){
title = s;
price = d;
}
public void getInfo(){
system.out.println(s+' -- '+ d);
}
}
class Test{
public static void main(String[] args) {
new Book("aa",12.5).getInfo();
}
}
上述代码通过匿名对象调用了类中的方法,但由于匿名对象没有对应的栈内存指向,所以只能使用一次,一次之后将成为垃圾,并且等待被GC回收释放。
简单Java类
对于简单Java类有如下基本要求:
- 类名称必须存在意义,例如:Book
- 类中的所有属性必须private封装,封装后的属性必须提供setter、getter
- 类中可以提供任意多个构造,但是必须保留一个无参构造方法
- 类中不允许出现任何输出语句,所有信息输出必须交给被调用出输出
- 类中需要提供一个取得对象完整信息的方法
开发Emp程序类
编写测试代码
数组
数组的基本概念
数组指的是一组相关变量的集合,本身也属于引用数据类型。
声明并开辟数组:
数据类型 数组名称[] = new 数据类型[长度] 或
数据类型[] 数组名称 = new 数据类型[长度]
当数组开辟空间之后,那么可以采用“数组名称[下标|索引]”的形式进行访问,但是所有数组的下标都是从0开始的,即:如果是3个长度的数组,那么下标的范围:0 ~ 2(0、1、2一共是三个内容)。如果访问的时候超过了数组的允许下标的长度,那么会出现数组越界异常(ArrayIndexOutOfBoundsException)。
定义数组:
// An highlighted block
class Test{
public static void main(String args[]){
int data[] = new int[3];
data[0] = 10;
data[1] = 20;
data[2] = 30;
for(int i = 0; i < data.length; i++){
system.out.print(data[i]+"--")
}
}
}
执行结果:10--20--30
以上给出的数组定义结构使用的是动态初始化的方式,即:数组会首先开辟内存空间,但是数组中的内容都是其对应数据类型的默认值,如果现在声明的是int型数组,则数组里面的全部内容都是其默认值:0。
由于数组是一种顺序的结构,并且数组的长度都是固定的,那么可以使用循环的方式输出,很明显需要知道for循环,而且在Java里面为了方便数组的输出提供有一个“数组名称.length”的属性,可以取得数组长度。
数组的引用传递
数组的静态初始化
-
格式一:数据类型 数组名称[] = {值,值,…}
-
格式二:数据类型 数组名称[] = new 数据类型[] {值,值,…}
求数组中的最大值
public class App {
public static void main( String[] args ) {
int[] arr = {10,9,34,25,12};
int max = arr[0];
for(int i = 1; i < 5; i++){
if(arr[i] > max){
max = arr[i];
}
}
System.out.println("最大值为:"+max);
}
}
数组与方法参数的传递
// An highlighted block
class Array{
public static void main(String args[]){
int data[] = new int[]{1,2,3};
change(data);
for(int x = 0; x<data.length; x++){
system.out.print(data[x]+"--")
}
}
public static void change(int emp[]){
for(int x = 0; x< emp.length; x++){
emp[x]*=2;
}
}
}
执行结果:2--4--6
数组的冒泡排序
// An highlighted block
冒泡排序(升序排序)基本原理:
原始数据:8,7,6,5,4,3,2,1
第一次排序:7,6,5,4,3,2,1,8 (取这些数字最大的放后面,比较了7次)
第二次排序:6,5,4,3,2,1,7,8 (去剩下7个数字中最大的放倒数第二个,比较了6次)
...
class Test{
public static void main(String[] args) {
int[] data = new int[]{2,1,9,0,5,3,7,6,8};
}
public static void sort(int[] data){
for(int x = 0; x < data.length-1; x++){ //这个地方只要循环7次即可,最后一次不需要了
for(int y = 0; y < data.length-1-x; y++){ //这个地方是总共最大需要比较7次,然后比较一次就可以减少比较一次
if(data[y] > data[y+1]){
int t =data[y];
data[y] = data[y+1];
data[y+1] = t;
}
}
}
}
public static void print(int[] arr){
for(int x = 0; x < arr.length ; x++){
system.out.print(arr[x]+"--");
}
}
}
二维数组
二维数组的定义语法
动态初始化:数据类型 数组名称[][] = new 数据类型[行的个数][列的个数];
静态初始化:数据类型 数组名称[][] = new 数据类型[][] {{值,值,值},{值,值,值}}
二维数组的列数不等情况
public class App {
public static void main( String[] args ) {
int[][] arr = {{10,9,34,25,12},{20,29,43},{8,0,5,7}};
System.out.println(arr[2].length);
System.out.println(arr.length);
}
}
结果:4
3
数组操作方法
- 数组复制
数组复制可以将一个数组的部分内容复制到另外一个数组之中。
System.arraycopy(源数组名称,源数组复制开始索引,目标数组名称,目标数组复制开始索引,长度)
// An highlighted block
public class ArrayDemo{
public static void main(String args[]){
int[] dataA = new int[]{1,2,3,4,5,6,7,8};
int[] dataB = new int[]{11,22,33,44,55,66,77,88};
System.arraycopy(dataA,4,dataB,2,3);
print(dataB);
}
public static void print(int[] emp){
for(int x = 0; x < emp.length;x++){
System.out.print(emp[x]+"、");
}
}
}
执行结果:11、22、5、6、7、66、77、88
- 数组排序
// An highlighted block
public class ArrayDemo{
public static void main(String args[]){
int[] data = new int[]{3,6,1,2,8,0};
java.util.Arrays.sort(data);
print(data);
}
pubilc static void print(int emp[]){
for(int x = 0 ; x < data.length ; x++){
system.out.print(emp[x]+"、")
}
}
}
执行结果:0、1、2、3、6、8
对象数组
数组是引用类型,而对象也是引用类型,所以如果是对象数组的话表示一个引用类型里面嵌套其他的引用类型。
- 对象数组动态初始化
-
对象数组静态初始化
-
对象数组内存关系
String类的基本概念
String类的两种实例化方式
- 直接赋值
// An highlighted block
String str = "www.baidu.com";
- 构造方法实例化
// An highlighted block
public String(String str) //构造方法里面依然需要穿一个String类对象
String str = new String("www.baidu.com");
注意
// An highlighted block
String str = ""; \\长度为0的字符串,这里不是null
String strA = " "; \\长度为1的字符串,内容是空格
字符串的比较
“==”是Java提供的关系运算符,主要的功能是进行数值相等判断的,如果用在了String对象上表示的是内存地址数值的比较;
“equals()”:是由String提供的一个方法,此方法专门负责进行字符串内容的比较。
内容比较操作(区分大小写):public boolean equals(String str);
实现字符串内容比较
字符串常量就是String的匿名对象
// An highlighted block
public class StringDemo{
public static void main(String args[]){
String str = "baidu";
system.out.println("baidu".equals(str));
}
}
执行结果:true
由于equals()方法试String类定义的,而类中的方法只有实例化对象才能调用,那么就可以得出一个结论:字符串常量就是String类的匿名对象。
所谓的String类对象直接赋值的操作,实际上就相当于将一个匿名对象设置了一个名字,但是唯一的区别是,String类的匿名对象是由系统自动生成的,不再由用户自己直接创建。
两种实例化方式的区别
1.分析直接赋值实例化String类对象的情况
// An highlighted block
public class StringDemo{
public static void main(Sting args[]){
String stra = "hello";
String strb = "hello";
String strc = "hello";
String strd = "yoo";
system.out.println(stra == strb);
system.out.println(stra == strc);
system.out.println(strb == strc);
system.out.println(stra == strd);
}
}
执行结果:true
true
true
false
通过上述程序发现,使用了直接赋值的实例化操作方式,设置的内容相同,只会开辟一块堆内存空间,并且会自动保存在对象池中以供下次重复使用,但是如果在直接赋值时内容与之前不一样,则会自动开辟新的堆内存空间。
2.分析构造方法实例化String类对象的情况
如果要明确的调用String类中的构造方法进行String类对象的实例化操作,那么一定要使用关键字new,而每当使用关键字new就表示要开辟新的堆内存空间,这块堆内存空间的内容就是传入到构造方法中的字符串数据。
// An highlighted block
String str = new String("hello");
首先每一个字符串都是一个Srting类的匿名对象,所以会在堆内存中开辟一块空间保存字符串"hello",然后使用关键字new,开辟另一块堆内存空间。然而真正使用的是关键字new开辟的堆内存,而之前的字符串常量的堆内存空间将不会有任何的栈内存指向,将会成为垃圾,等待被GC回收。
不自动保存对象池操作
手工入池
字符串一旦定义则不可改变
String类的常用方法
字符与字符串
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String(char[] value) | 构造 | 将字符数组变为String类对象 |
2 | public String(char[] value, int offset, int count) | 构造 | 将部分字符数组变为String |
3 | public char charAt(int index) | 普通 | 返回指定索引对应的字符信息 |
4 | public char[] toCharArray() | 普通 | 将字符串以字符数组的形式返回 |
去查指定索引的字符
// An highlighted block
String str = "hello";
char c =str.charAt(0);
system.out.println(c)
执行结果:h
字符数组与字符串的转换
// An highlighted block
String str = "hello";
char[] data =str.toCharArray();
for(int x = 0 ; x <data.length ; x++){
system.out.println(data[x]+"、")
}
执行结果:h、e、l、l、o、
将字符串转化为大写
// An highlighted block
String str = "hello";
char[] data =str.toCharArray();
for(int x = 0 ; x <data.length ; x++){
data[x] -= 32;
}
system.out.println(new String(data));
system.out.println(new String(data,1,2))
执行结果:HELLO
EL
给定一个字符串,判断其是否由数字组成
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "04151561597";
if(isNumber(str)){
System.out.println("都是数字");
}else {
System.out.println("有非数字");
}
}
public static boolean isNumber(String str){
char[] chars = str.toCharArray();
for(int i = 0 ; i < chars.length ; i++){
//if('0'> chars[i] || chars[i] > '9')
if(57 < chars[i] || chars[i] < 48){ //这里根据数字的ascii码表
return false;
}
}
return true;
}
}
执行结果:h、e、l、l、o、
字节与字符串
字节使用byte描述,字节一般主要用于数据的传输或编码的转换,而String类里面提供了将字符串变成字节数组的操作,就是为了传输以及编码转换。
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String(byte[] bytes) | 构造 | 将全部字节数组变为字符串 |
2 | public String(byte[] bytes, int offset, int count) | 构造 | 将部分字节数组变为String |
3 | public byte[] getBytes() | 普通 | 将字符串变为字节数组 |
4 | public byte[] getBytes(String charsetName) throws UnsupportedEncodingException | 普通 | 进行编码转换 |
// An highlighted block
String str = "helloworld";
byte[] data =str.getBytes();
for(int x = 0 ; x <data.length ; x++){
data[x] -= 32;
}
system.out.println(new String(data));
system.out.println(new String(data,5,5))
执行结果:HELLOWORLD
WORLD
字符串的比较
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean equals(String value) | 普通 | 进行相等判断,区分大小写 |
2 | public boolean equalsIgnoreCase(String anotherString) | 普通 | 进行相等判断,不区分大小写 |
3 | public int compareTo(String anotherString) | 普通 | 先读取出字符串的第一个“字母”进行比较,比较的方法是ascii码表的值(字符所对应的十进制值),如果前面的大那么返回1,后面的大返回-1;此位置相同,继续比较下一位,直到最后一位,如果都相同的话,就返回0; |
// An highlighted block
public class Message {
public static void main(String[] args) {
String stra = "Hello";
String strb = "HEllo";
System.out.println(stra.compareTo(strb));
if(stra.compareTo(strb) > 0){
system.out.println("大于")
}
}
}
字符串的查找
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean contains(String s) | 普通 | 判断指定内容是否存在 |
2 | public int indexOf(String str) | 普通 | 由前向后查找字符串的位置,如果找到了则返回(第一个字母)位置索引,找不到返回-1 |
3 | public int indexOf(String str,int fromIndex) | 普通 | 由指定位置从前向后查找指定字符串的位置,找不到返回-1 |
4 | public int lastIndexOf(String str) | 普通 | 由后向前查找指定字符串的位置,找不到返回-1 |
5 | public int lastIndexOf(String str,int fromIndex) | 普通 | 从指定位置由后向前查找字符串的位置,找不到返回-1 |
6 | public int startsWith(String str) | 普通 | 判断是否以指定字符串开头 |
7 | public int startsWith(String str,int toffset) | 普通 | 从指定位置开始判断是否以指定的字符串开头 |
8 | public int endsWith(String str) | 普通 | 判断是否以指定的字符串结尾 |
使用indexOf()查找
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
system.out.println(str.indexOf("world"));
system.out.println(str.indexOf("l"));
system.out.println(str.indexOf("l",5));
system.out.println(str.lastIndexOf("l"));
}
}
执行结果:5
2
8
8
使用indexOf()判断子字符串是否存在
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
if(str.indexOf("world") != -1){
system.out.println("存在");
}
}
}
执行结果:存在
使用contains()判断子字符串是否存在
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
if(str.contains("world")){
system.out.println("存在");
}
}
}
执行结果:存在
开头或结尾判断
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "##@@hello**";
system.out.println(str.stratsWith("##"));
system.out.println(str.stratsWith("@@",2));
system.out.println(str.endsWith("**"));
}
}
执行结果:true true true
字符串的替换
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String replaceAll(String regex,String replacement) | 普通 | 用新的内容替换全部旧的内容 |
2 | public String replaceFirst(String regex,String replacement) | 普通 | 替换首个满足条件的内容 |
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
String strA = str.replaceAll("l","_");
String strB = str.replaceFirst("l","_");
system.out.println(strA);
system.out.println(strB);
}
}
执行结果:he__owor_d
he_loworld
字符串的截取
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String substring(int beginIndex) | 普通 | 从指定索引截取到结尾 |
2 | public String substring(int beginIndex,int endIndex) | 普通 | 截取部分子字符串的数据 |
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
String strA = str.substring(5);
String strB = str.substring(0,5);
system.out.println(strA);
system.out.println(strB);
}
}
执行结果:world
hello
字符串的拆分
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String[] split(String regex) | 普通 | 按照指定的字符串进行全部拆分 |
2 | public String[] split(String regex,int limit) | 普通 | 按照指定的字符串进行部分拆分,最后的数组长度由limit决定(如果能拆分的结果很多,数组长度才会由limit决定),即前面拆,后面不拆 |
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "hello yootk nihao mldn";
String result[] = str.split(" ");
for(int i = 0; i < result.length; i++){
system.out.print(result[i]+"、");
}
}
}
执行结果:hello、yootk、nihao、mldn、
如果使用空字符串则表示根据每个字符拆分
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "hello yootk";
String result[] = str.split(""); //字符串全部进行拆分
for(int i = 0; i < result.length; i++){
system.out.print(result[i]+"、");
}
}
}
执行结果:h、e、l、l、o、 、y、o、o、t、k、
拆分为指定的个数
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "hello yootk nihao mldn";
String result[] = str.split(" ",2);
for(int i = 0; i < result.length; i++){
system.out.println(result[i]);
}
}
}
执行结果:hello
yootk nihao mldn
有关正则表达式的拆分
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "192.168.1.2";
String result[] = str.split("\\."); //以 . 来拆分
for(int i = 0; i < result.length; i++){
system.out.print(result[i]+"、");
}
}
}
执行结果:192、l68、1、2 、
二次拆分
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "张三:20|李四:21|王五:22";
String result[] = str.split("\\|"); //以 | 来拆分
for(int i = 0; i < result.length; i++){
String temp[] = result[i].split(":");
system.out.print("姓名:"+temp[0]+",年龄: "+temp[1]);
}
}
}
执行结果:姓名:张三,年龄:20
姓名:李四,年龄:21
姓名:王五,年龄:22
其他方法
NO. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean concat(String str) | 普通 | 字符串连接,与“+”类似 |
2 | public String toLowerCase() | 普通 | 转小写 |
3 | public String toUpperCase() | 普通 | 转大写 |
4 | public String trim() | 普通 | 去掉字符串中左右两边的空格,中间空格保留 |
5 | public int length() | 普通 | 取得字符串长度 |
6 | public String intern() | 普通 | 数据入池 |
7 | public boolean isEmpty() | 普通 | 判断是否是空字符串(不是null,而是"",长度0) |
字符串连接
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "hello".concat("world");
system.out.println(str);
}
}
执行结果: helloworld
转大小写操作
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "(*(*Hello(*(*";
system.out.println(str.toUpperCase());
system.out.println(str.toLowerCase());
}
}
执行结果: (*(*HELLO(*(*
(*(*hello(*(*
去掉左右空格
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = " hello world ";
system.out.println("【"+ str+"】");
system.out.println("【"+ str.trim()+"】");
}
}
执行结果: 【 hello world 】
【hello world】
删除全部空格
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = " hello world ";
system.out.println(str.replaceAll(" ",""));
}
}
执行结果: helloworld
取得字符串长度
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
system.out.println(str.length());
}
}
执行结果: 10
判断是否为空字符串
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
system.out.println(str.isEmpty());
system.out.println("".isEmpty());
}
}
执行结果: false
true
实现首字母大写操作
// An highlighted block
public class Message {
public static void main(String[] args) {
String str = "helloworld";
system.out.println(inincap(str));
}
public static String initcap(String s){
return s.substring(0,1).toUpperCase()+s.substring(1);
}
}
执行结果: Helloworld
this关键字
调用本类属性
在一个类中定义的方法中可以直接访问类中的属性,但是很多时候有可能会出现方法参数名称与属性名称重复的情况,所以此时就需要利用“this.属性”的形式明确的指明要调用的是类中的属性而不是方法的参数。
// An highlighted block
class Book{
private String title;
private double price;
public Book(String title,double price){
this.title = title; //this.属性表示的是本类属性,这样即使与方法中的参数重名也可以明确定位
this.price = price;
}
public String getInfo(){
return "书名: "+title+",价格:"+price;
}
}
调用本类方法
在一个类之中,可以使用this调用两种方法(普通方法、构造方法),而在要调用本类方法也就分为两种形式:
调用本类普通方法:在之前强调过,如果现在要调用的是本类方法,则可以使用“this.方法()”调用;
调用本类构造方法:在一个构造中要调用其他构造使用“this()”调用
// An highlighted block
class Book{
private String title;
private double price;
public Book(){
system.out.println("一个新的book类产生"); //此处想象成100行代码
}
public Book(String title,double price){
this(); //this调用Book()构造方法
this.title = title; //this.属性表示的是本类属性,这样即使与方法中的参数重名也可以明确定位
this.price = price;
}
public void print(){
system.out.println("aaa");
}
public String getInfo(){
this.print(); //this调用普通方法
return "书名: "+title+",价格:"+price;
}
}
public class Message {
public static void main(String[] args) {
Book book = new Book("java开发",89.2);
system.out.println(book.getInfo());
}
}
执行结果:一个新的book类产生
aaa
书名:java开发,价格:89.2
关于this调用构造方法的限制
- 使用"this()"调用构造方法形式的代码只能够放在构造方法的首行;
- 进行构造方法相互调用时,一定要保留调用的出口
举一个错误实例:
// An highlighted block
class Book{
private String title;
private double price;
public Book(){
this("aa",15.2)
system.out.println("一个新的book类产生"); //此处想象成100行代码
}
public Book(String title,double price){
this();
this.title = title;
this.price = price;
}
public String getInfo(){
return "书名: "+title+",价格:"+price;
}
}
可以细心发现,上述代码形成一个构造方法调用的死循环状态,所以this调用构造方法时,一定要留有出口。
表示当前对象
this关键字在应用的过程之中有一个最为重要的概念 —— 当前对象,而所谓的当前对象指的就是当前正在调用类中方法的实例化对象
// An highlighted block
class Book{
public void print(){
system.out.println("this == "+this);
}
}
public class Message{
public static void main(String args[]){
Book bookA = new Book();
system.out.println("bookA =="+bookA);
bookA.print();
Book bookB = new Book();
system.out.println("bookB =="+bookB);
bookB.print();
}
}
执行结果:
bookA ==chapter9.Book@4554617c (注意:地址可能不一样,但一定相等)
this == chapter9.Book@4554617c
bookB ==chapter9.Book@74a14482
this == chapter9.Book@74a14482
引用传递
引用传递基本概念
引用传递是:同一块堆内存空间可以被不同的栈内存所指向,不同栈内存可以对同一堆内存进行内容的修改。
基本类型的传递
// An highlighted block
public class Message {
public static void main(String[] args) {
int num = 5;
isNumber(num); //此参数num只是变量num的拷贝
System.out.println(num);
}
public static void isNumber(int str){
str = 100;
}
}
执行结果:5
引用类型的传递
// An highlighted block
class Book{
private int num = 10;
public Book(int num){
this.num = num;
}
public void set(int num){
this.num = num;
}
public int getNum(){
return this.num;
}
}
public class Message{
public static void main(String args[]){
Book bookA = new Book(30);
fun(bookA); //此时bookA是一份指向一块堆内存引用的拷贝
system.out.println(bookA.getNum())
}
public static void fun(Book tem){
tem.setNum(100);
}
}
执行结果:100
String类型的传递(该写法现在存在语法上的错误,无意义直接跳过)
// An highlighted block
public class Message{
public static void main(String args[]){
String msg = "hello";
fun(msg); //此时msg是一份指向一块堆内存引用的拷贝
system.out.println(msg)
}
public static void fun(String tem){
tem = "world"; //tem是一份指向"hello"的引用地址的拷贝,tem先指向"hello"后指向"world"
}
}
执行结果:hello
结合String和引用类型的传递
// An highlighted block
class Book{
private String info = "此内容无用";
public Book(String info){
this.info = info;
}
public void set(String info){
this.info = info;
}
public String getInfo(){
return this.info;
}
}
public class Message{
public static void main(String args[]){
Book bookA = new Book("hello");
system.out.println(bookA.getInfo());
fun(bookA); //此时bookA是一份指向一块堆内存引用的拷贝
system.out.println(bookA.getInfo());
}
public static void fun(Book tem){
tem.setInfo("world");
}
}
执行结果:hello
world
总而言之,记住一点,方法里的参数是一份地址的拷贝!!!
对象比较
如果有两个数字要判断是否相等,可以使用"=="完成,如果是字符串要判断是否相等可以使用equals(),那么要判断两个自定义的类是否相等,则必须要实现类对象中所有属性内容的比较。
基础的比较方式
// An highlighted block
class Book{
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
}
public class Message {
public static void main(String[] args) {
Book b1 = new Book("java开发",79.8);
Book b2 = new Book("java开发",79.8);
if(b1.getTitle().equals(b2.getTitle()) && b1.getPrice() == b2.getPrice()){
System.out.println("是同一个对象");
}else {
System.out.println("不是同一个对象");
}
}
}
执行结果:是同一个对象
对象比较的实现
// An highlighted block
class Book{
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public boolean compare(Book book){
if(book == null){
return false;
}
if(this == book){
return true;
}
if(this.title.equals(book.title) && this.price == book.price){
return true;
}else{
return false;
}
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
}
public class Message {
public static void main(String[] args) {
Book b1 = new Book("java开发",79.8);
Book b2 = new Book("java开发",79.8);
if(b1.compare(b2)){
System.out.println("是同一个对象");
}else {
System.out.println("不是同一个对象");
}
}
}
执行结果:是同一个对象
static关键字
static定义属性
如果类中的某个属性希望定义为公共属性(所有对象都可以使用的属性),则可以在声明属性前加上static关键字。
// An highlighted block
class Book{
private String title;
private double price;
static String pub = "小学";
public Book(String title,double price){
this.title = title;
this.price = price;
}
}
public class TestDemo{
public static void main(String args[]){
Book a = new Book("aa",10.1);
Book b = new Book("bb",10.1);
a.pub = "中学";
system.out.println(b.pub);
}
}
执行结果:中学
static属性与非static属性还有一个最大区别,所有的非static属性必须产生实例化对象才可以访问,但是static属性不受实例化对象的控制,也就是说,在没有实例化对象产生的情况下,依然可以使用static属性。
// An highlighted block
class Book{
private String title;
private double price;
static String pub = "小学";
public Book(String title,double price){
this.title = title;
this.price = price;
}
}
public class TestDemo{
public static void main(String args[]){
system.out.println(Book.pub);
}
}
执行结果:小学
常见内存区域。
在Java中主要存在4块内存空间,这些内存空间的名称及作用如下。
- 栈内存空间:保存所有的对象名称(更准确的说是保存引用的堆内存空间地址)
- 堆内存空间:保存每个对象的具体属性内容
- 全局数据区:保存static类型的属性
- 全局代码区:保存所有的方法定义
static定义方法
在定义类的普通方法时也可以使用static进行定义,那么很明显,使用static定义的方法也可以在没有实例化对象产生的情况下由类名称直接进行调用。
// An highlighted block
class Book{
private String title;
private double price;
static String pub = "小学";
public Book(String title,double price){
this.title = title;
this.price = price;
}
public static void setpub(String p){
pub = p; //注意staitc修饰的属性不能使用this
}
}
public class TestDemo{
public static void main(String args[]){
Book.setpub("中学");
Book a = new Book("aa",15.1);
system.out.println(a.pub);
}
}
执行结果:中学
- static方法不能直接访问非static属性或方法,只能调用static属性或方法;
- 非static方法可以访问static的属性或者方法,不受任何限制。
原因:
- 所有的非static定义的结构,必须在类已经明确产生实例化对象时才会分配堆空间,才可以使用;
- 所有的static定义的结构,不受实例化对象的控制,即可以在没有实例化对象的时候访问。
static的实际应用
通过之前的了解,可以发现static关键字有如下特点:
- 不管有多少个对象,都使用同一个static属性
- 使用static方法可以避免实例化对象调用方法的限制
实现类实例化对象个数的统计
// An highlighted block
class Book{
private static int num = 0;
public Book(){
num++;
system.out.println("这是第"+num+"个对象");
}
}
public class TestDemo{
public static void main(String args[]){
new Book();new Book();
}
}
执行结果:这是第1个对象
这是第2个对象
代码块
在程序编写之中可以直接使用“{}”定义一段语句,那么根据此部分定义的位置以及声明的关键字的不同,代码块一共可以分为四种:普通代码块、构造块、静态块、同步代码块(多线程时用到)。
普通代码块
如果一个代码块写在方法里,就称它为普通代码块。
// An highlighted block
public class TestDemo{
public static void main(String args[]){
{ //普通代码块
int num = 10;
system.out.println("num = " + num);
}
int num = 10;
system.out.println("num = " + num);
}
}
构造块
如果将一个代码块写在一个类里,这个代码块就称为构造块。
// An highlighted block
class Book{
public Book(){
system.out.println("【A】Book类的构造方法")
}
{
system.out.println("【B】Book类的构造块");
}
}
public class TestDemo{
public static void main(String args[]){
new Book();
new Book();
}
执行结果:【B】Book类的构造块
【A】Book类的构造方法
可以发现,构造块在每一次实例化类对象时都会被调用,而且优于构造方法执行。
静态块
如果一个代码块使用static进行定义,就称其为静态块。
// An highlighted block
class Book{
private String msg;
public Book(){
system.out.println("【A】Book类的构造方法")
}
{
system.out.println("【B】Book类的构造块");
}
static{
msg = "Hello";
system.out.println("【C】Book类的静态块");
}
}
public class TestDemo{
public static void main(String args[]){
new Book();
new Book();
}
执行结果:【C】Book类的静态块
【B】Book类的构造块
【A】Book类的构造方法
【B】Book类的构造块
【A】Book类的构造方法
通过运行结果可以发现当有多个实例化对象产生时,静态块会优先调用,而且只调用一次。静态块的主要作用一般可以为static属性初始化。