疯狂Java学习笔记(008)

回顾

一、数组

1.数组的概念:

  同种数据类型若干个数值的组合.称为数组.

  实际上就是多个内存空间(变量)的组合.

  数组的元素值可以在数据类型的表数范围内发生变化!!! 

2.数组的元素:

  element:数组中的数值就称为数组的元素!

3.数组的长度:

  length:就是数组中元素的个数.

4.元素的索引:

  index:数组中元素的编号.从0开始.最大值是数组的长度-1.

5.数组的初始化方式:

  初始化指的是数组中的元素第一次赋值,以后再赋值就不是初始化.

  1.> 动态初始化:只指定数组的长度.由系统赋默认初始值(0/0.0/false/null).

    int[] arr = new int[5];

    在堆空间中开辟5个能存放int型数据的空间,赋予默认的初始值,并把地址值返回给变量arr.

  2.静态初始化:在定义数组时指定数组的初始化值.此时不能指定数组的长度!(静态和动态不能同时使用!!)

    int[] arr = new int[]{1,2,3};

    //在堆空间中开辟3个存放int型数据的空间,把1,2,3放进去,然后把内存地址值返回给变量arr.

    简化写法:

    int[] arr = {1,2,3};

5. 数组类型的变量直接打印的结果:

  以int型数组为例:

  [I@xxxx

  [ :指的是变量中保存的内存空间是数组类型

  I :指的是数组的类型是int型(也就是数组元素的类型)

  @ :固定的分隔符

  xxxx :一串字符串,十六进制.由内存地址计算而来的.

  用途:比较两个数组是否是同一个数组!!(内存地址值相同,肯定是同一个数组)

二、jvm内存结构:

1.栈:

    凡是方法中定义的变量(包括方法的形参)都是在栈中.

    栈中的变量没有初始化值,使用之前必须手动赋值.

    方法执行,变量就存在,方法执行完,变量所占用的空间自动释放!

2.堆:

    凡是使用new关键字创建出来的对象都是在堆中!

    堆中的变量都有初始化值:(0/0.0/false/null)

    堆中的变量随着对象的存在而存在.当对象没有任何引用指向它时,它所占用的空间由垃圾回收器负责回收. 

3.方法区:

    .class字节码文件和方法定义

    System

    Scanner

    Math

4.本地方法栈:

5.程序计数器:

三、数组的遍历:traversal

  依次访问数组中每个元素的一种机制!

  使用循环(循环的是元素的索引)就可以遍历每个元素!

  循环的顺序:从前往后,从后往前.

  ***数组操作的两个常见异常:

  1.ArrayIndexOutOfBoundsException

    索引超出了正常值[0,数组的长度-1]范围:

  2.NullPointerException

    一个数组变量,被赋予了null值,使用它去访问数组中的元素,发生空指针异常.

作业:

1.数组初始化的两种格式:

2.什么是数组的遍历?

3.jvm虚拟机的内存划分成几个区域,各个区域中的变量都有什么特点?

4.定义一个方法,用于将一个int数组的偶数索引的元素抽取(extract)出来.

/*

    定义一个方法,用于将一个int数组的偶数索引的元素抽取出来.
    计算偶数索引元素的个数,创建新的数组,
    遍历源数组,判断符合条件的,赋值给新数组
    
*/
public class ExtractEvenIndexDemo{
    public static void main(String[] args){
        int[] arr = {1,2,3,4,5,6,7,8,9,0};
        int[] dest = extractEvenIndex(arr);
        printArray(dest);
    }
    
    public static void printArray(int[] arr){
        for(int i = 0;i<arr.length;i++){
            System.out.print(arr[i]);
            if(i != arr.length - 1){
                System.out.print(",");
            }
        }
        System.out.println();
    }
    
    
    //返回值类型:int[]
    //形参列表:int[]
    public static int[] extractEvenIndex(int[] arr){
        //计算偶数索引的个数
        int[] dest = new int[(arr.length + 1) / 2];
        //遍历源数组,判断
        int index = 0;
        for(int i = 0;i<arr.length;i++){
            if(i % 2 == 0){
                dest[index++] = arr[i];
            }
        }
        return dest;
    }
    
}

5.定义一个方法,用于将一个数组逆序.注意方法的返回值类型?即:逆序是否是产生了新的数组?

/*
    5.定义一个方法,用于将一个数组逆序.注意方法的返回值类型?即:逆序是否是产生了新的数组?
    思路:
        并没有产生新的数组,而是把源数组中的元素逆序了!!
    重点:有多少次两两交换??
        4 -> 2
        5 -> 2
        6 -> 3
        7 -> 3
        ...
        
*/
public class ReverseArrayDemo{
    public static void main(String[] args){
        int[] arr = {1,2,3,4,5,6,7};
        reverse(arr);
        printArray(arr);
        
    }
    
    public static void printArray(int[] arr){
        for(int i = 0;i<arr.length;i++){
            System.out.print(arr[i]);
            if(i != arr.length - 1){
                System.out.print(",");
            }
        }
        System.out.println();
    }
    //没有返回值:void
    //形参列表:int[]
    public static void reverse(int[] arr){
        //计算两两交换的次数
        int count = arr.length / 2 ;
        //循环交换两个元素
        for(int i = 0;i<count;i++){
            //交换两个元素
            int temp = arr[i];
            arr[i] = arr[arr.length - 1 - i];
            arr[arr.length - 1 - i] = temp;
        }
        
    }
}

6.定义一个方法,用于将一个数组的所有元素变成它原来的元素和它前一个元素的和,(第一个元素除外!)

    即:若原数组为:[1,2,3,4,5]

    则方法执行后,数组变为:[1,3,5,7,9]

    注意:方法中不能创建新的数组!!!

/*
    1.从前往后遍历:用临时变量保存当前要修改的元素的值,更改后面元素的时候,就可以使用原来元素的值.
    2.从后往前遍历!用元素的值和它前一个元素的和替换当前元素!
*/
public class Demo1{
    public static void main(String[] args){
       int[] arr = {1,2,3,4,5};
       printArray(arr);
       // change1(arr);
       change2(arr);
       printArray(arr);
    }
    public static void printArray(int[] arr){
       for(int i = 0;i<arr.length;i++){
           System.out.print(arr[i]);
           if(i != arr.length - 1){
              System.out.print(",");
           }
       }
       System.out.println();
    }
   
    //第二种方式:从后往前遍历
    public static void change2(int[] arr){
       //
       for(int i = arr.length - 1;i>0;i--){
           arr[i] = arr[i] + arr[i - 1];
       }
    }
   
    //第一种方式:使用临时变量保存上一次被覆盖的元素的值
    public static void change1(int[] arr){
       //定义一个变量,初始值为第一个元素的值.
       int temp = arr[0];
       for(int i = 1;i<arr.length;i++){
           //先把当前元素的值保留下来.
           int x = arr[i];
           //替换当前元素的值
           arr[i] = arr[i] + temp;
           //把当前元素原来的值,赋值给temp
           temp = x;
       }
    }
}

7.定义一个方法,用于模拟裁判打分的程序:一共有5个裁判打分,去掉一个最高分,去掉一个最低分.

取剩余分数的平均值为最终的得分!!!注意:分数的数据类型!!!

重点:自定义方法之间的互相调用!!!

/*
定义一个方法,用于模拟裁判打分的程序:一共有5个裁判打分,去掉一个最高分,去掉一个最低分.
取剩余分数的平均值为最终的得分!!!注意:分数的数据类型!!!
 
方法之间是互相调用的关系:
    自定义方法中可以使用jdk提供好的方法(比如打印方法),也可以调用自定义方法!!!
*/
public class MarkDemo{
    public static void main(String[] args){
       int[] scores = {10,20,10,15,25};
       System.out.println("最终得分是: " + mark(scores));
    }
   
    //double
    //int[] scores
    public static double mark(int[] scores){
       //计算分数之和
       // System.out.println("abc");//自定义方法中调用jdk提供的方法
       int sum = getSum(scores);//自定义方法中调用自定义方法!!
      
       //减去最高分,最低分
       //求平均分返回
       return (sum - getMax(scores) - getMin(scores)) / 3.0;
    }
   
    //最高分(最大值)
    public static int getMax(int[] scores){
       int max = scores[0];
       for(int i = 1;i<scores.length;i++){
           if(scores[i] > max){
              max = scores[i];
           }
       }
       return max;
    }
   
    //最低分(最小值)
    public static int getMin(int[] scores){
       int min = scores[0];
       for(int i = 1;i<scores.length;i++){
           if(scores[i] < min){
              min = scores[i];
           }
       }
       return min;
    }
   
    //求数组所有元素之和
    public static int getSum(int[] scores){
       //定义变量,初始值为0
       int sum = 0;
       for(int i = 0;i<scores.length;i++){
           sum += scores[i];
       }
       return sum;
    }
}

8.定义一个方法,用于取出一个数组中所有偶数元素组成的数组!

/*
    定义一个方法,用于取出一个数组中所有偶数元素组成的数组!
    首先计算偶数元素的个数,用个数创建一个新的数组,
    遍历源数组,把符合条件的元素赋值给新数组
   
    两次遍历:
       1.求偶数元素的个数
       2.赋值
*/
public class ExtractEvenElementDemo{
    public static void main(String[] args){
       int[] src = {1,2,3,4,5,6,67,7,8};
       printArray(src);
      
       int[] dest = extractEvenElement(src);
      
       printArray(dest);
    }
    public static void printArray(int[] arr){
       for(int i = 0;i<arr.length;i++){
           System.out.print(arr[i]);
           if(i != arr.length - 1){
              System.out.print(",");
           }
       }
       System.out.println();
    }
   
    //返回值类型:int[]
    //形参列表:int[] src
    public static int[] extractEvenElement(int[] src){
       //第一次遍历,求偶数元素的个数
       int len = 0;
       for(int i = 0;i<src.length;i++){
           if(src[i] % 2 == 0){
              len++;
           }
        }
      
       //创建新数组
       int[] dest = new int[len];
       //第一次遍历,求偶数元素的个数
       int index = 0;
       for(int i = 0;i<src.length;i++){
           if(src[i] % 2 == 0){
              dest[index++] = src[i];
           }
       }
       return dest;
    }
}
 

9.定义一个方法,用于取出一个数组中指定索引范围的元素组成的数组!!

    例如:可以取出一个数组第2个到第5个索引组成的数组!

 
/*
定义一个方法,用于取出一个数组中指定索引范围的元素组成的数组!!
    例如:可以取出一个数组第2个到第5个索引组成的数组!
思路:
    根据传递进来的两个数,确定要截取的范围:
    用这个范围的长度去创建一个新数组,
    遍历源数组,赋值!
   
*/
public class GetArrayDemo{
    public static void main(String[] args){
       int[] arr = {1,2,3,4,5,6,7,8,9,0};
       int[] dest = getArray(arr,2,5);
       printArray(dest);
    }
   
    public static void printArray(int[] arr){
       for(int i = 0;i<arr.length;i++){
           System.out.print(arr[i]);
           if(i != arr.length - 1){
              System.out.print(",");
           }
       }
       System.out.println();
    }
   
    //int[]
    //int[] arr,int start,int end
    public static int[] getArray(int[] arr,int start,int end){
       //计算目标元素的个数
       int len = end - start + 1;
       //创建新数组
       int[] dest = new int[len];
       //遍历源数组赋值
       int index = 0;
       for(int i = start;i<=end;i++){
           dest[index++] = arr[i];
       }
       //
       return dest;
    }
}
 
 

选作题:

用数组实现:产生一个有5个范围在(1-100)之间的随机数元素的数组,要求元素不能重复!!!

/*
选作题:
用数组实现:产生一个有5个范围在(1-100)之间的随机数元素的数组,要求元素不能重复!!!
思路:
    先产生一个有默认值的数组,
    遍历,每次都产生一个随机数,拿这个随机数到数组中判断
       如果已经存在,就再次产生一个随机数,再次进行判断
       如果不存在,就直接给当前的元素赋值!
*/
public class GetRandomArrayDemo{
    public static void main(String[] args){
       int[] arr = new int[5];//0
      
       //遍历数组,产生随机数,判断,符合条件再对元素赋值
       for(int i = 0;i<arr.length;i++){
           int r = (int)(Math.random() * 100);
           //判断是否已经在数组中!
           /*
           while(exists(arr,r)){ //true ,表明已经存在,再次产生一个新的随机值!
              r = (int)(Math.random() * 100) + 1;
           }
           */
           //优化后的方法,需要三个参数,第二个参数表明比较的范围
           while(exists2(arr,i,r)){ //true ,表明已经存在,再次产生一个新的随机值!
              r = (int)(Math.random() * 100);
           }
          
           //false意味着数组中不存在这个随机值,那就直接赋值
           arr[i] = r;
       }
       //查看
       printArray(arr);
    }
   
    public static void printArray(int[] arr){
       for(int i = 0;i<arr.length;i++){
           System.out.print(arr[i]);
           if(i != arr.length - 1){
              System.out.print(",");
           }
       }
       System.out.println();
    }
   
    //优化后的方法:第二个参数表示比较的范围:只比较当前数组中已经赋值的元素!!!
    //优点:1.减少了比较的次数.2.避免了初始值是0值的问题!!!
    public static boolean exists2(int[] arr,int count,int value){
       //遍历数组,比较count之前的值是否和value相等
       for(int i = 0;i<count;i++){
           if(arr[i] == value){
              return true;
           }
       }
       //程序走到这里,说明for循环中没有碰到相同的数(即:value在数组中不存在)
       return false;
    }
   
    //boolean
    //int[] arr,int value
    public static boolean exists(int[] arr,int value){
       //遍历数组,如果遇到相同的值,就返回true,否则返回false
       for(int i = 0;i<arr.length;i++){
           if(arr[i] == value){
              return true;
           }
       }
       //程序走到这里,说明for循环中没有碰到相同的数(即:value在数组中不存在)
       return false;
    }
}
 

 -----------------------------------------------------------------------new-----------------------------------------------------------------

对象

一、参数传递的原则:值传递

  Java中,方法调用时,不论形参是基本数据类型还是引用数据类型,

  实际上都是实参的副本传递给方法的形参!!

  数组类型的参数,在方法体外能看到方法体中对数组元素的更改!!

  基本数据类型,在方法体外,看不到方法体中对数据的更改!!

二、面向对象的概念:

  1.面向对象编程:  

  OOP:object oriented programming,也就是在程序中,直接操作的是对象,而不是一个个的函数!

  

  和此相对应的思想是:面向过程编程思想:procedual(过程化)

  要实现一个功能,每一步的操作都必须自己实现.都是通过一个个的函数调用实现!

  (C语言就是典型的过程化语言!)

  面向对象编程,考虑的是谁有我想要实现的功能:

  举例:

    组装电脑:

    喝咖啡:

    建造汽车:

  2.面向对象思想的好处:

    1> 让程序员从执行者变成了指挥者

    2> 更符合人类思维的编程思想!

    3> 将复杂的事情简单化!

  面向对象开发,实际上就是维护对象之间的关系:

  例如:张三会开锁,李四会撬门,王五会开叉车.

if(张三.开锁() == ok){
    王五.开叉车();
}else if(李四.撬门() == ok){
    王五.开叉车();
}else{
    撤退!!!
}

  3.面向对象的三大特点:

    封装,继承,多态  -- 后面详讲!!!

三、类和对象:

  1.类:是对现实世界中事物的一种抽象表示!

  2.现实世界中事物的描述方法:

    1>静态的属性信息:颜色,价格,尺寸大小...

    2>功能或者行为:run,eat...

  3.Java中使用类来描述事物:

      电脑:          Java类:

      属性信息    ->  成员变量:

      功能或者行为->  成员方法:

例如:

public class Computer{
    //成员变量
    String color = "black";
    double price = 2000.0;
    int size = 16;
    //成员方法
    public void playMusic(){...}
    public void playMovie(){...}
}

4.类是一类事物的抽象形式:

  如何来的?

  应该是现实真实存在的一类事物,或者是脑海中的一个概念!

  将它们共同的属性和行为抽取出来就是类!!

  类一般情况下,都不能直接使用.

必须创建对象!!

从类到具体的对象.称为实例化过程(创建).

Person类定义的过程:

 

Dog类定义的过程:

四、类成员的写法:

  1.成员变量:

      和之前定义变量的规则一样

      写在类中,成员方法的外边

  2.成员方法:

      和之前定义方法的规则一样

      暂时去掉static

NoteBook.java

/*
    NoteBook类的定义:
    一个类一般是不能直接使用的,我们使用的是类所创建的对象.
    使用new关键字,就可以创建一个类的对象(实例化的过程).
   
    一般情况下是在main方法中创建对象.
    main方法可以写在普通类中,或者是一个单独的测试类中.
   
    创建对象的语法:
       类名 对象名 = new 类名();
   
    对象的使用:
       访问对象的成员变量
       调用对象的成员方法
   
    main方法和普通类没有所属关系,一般都会单独的放到一个专门的测试类中.
    这个测试类主要目的就是为了放main方法.!!!
   
*/
public class NoteBook{
    //成员变量
    String brand = "ASUS";
    int memory = 8;
    int price = 6000;
    String owner = "张三";
   
    //成员方法
    public void type(String content){
       System.out.println("正在打印: " + content);
    }
    //
    public void playMusic(String name){
       System.out.println("播放歌曲: " + name);
    }
   
    //显示自己的属性信息
    public void showInfo(){
       System.out.println("brand : " + brand + ", memory : " + memory + ", price : " + price + ", owner : " + owner);
    }
   
    /*
    //在普通类中写main方法
    public static void main(String[] args){
       //在这里创建对象
       NoteBook book1 = new NoteBook();
      
       //访问book1 的成员变量
       System.out.println("book1.brand : "+ book1.brand);
       System.out.println("book1.price: " + book1.price);
       System.out.println("book1.memory: " + book1.memory);
       System.out.println("book1.owner : " + book1.owner);
      
       //调用book1的成员方法
       book1.type("hello world");
       book1.playMusic("<<我站在草原望北京>>");
       book1.showInfo();
      
    }
    */
}

五、对象的创建

格式:

类名 对象名 = new 类名();

NoteBookDemo.java

/*
    NoteBook类的测试类:
    如果在源文件中出现了没有经过编译的源文件,那就一起编译!!!
   
*/
public class NoteBookDemo{
    public static void main (String[] args){
       //创建对象
       NoteBook book1 = new NoteBook();
      
       //访问book1 的成员变量
       System.out.println("book1.brand : "+ book1.brand);
       System.out.println("book1.price: " + book1.price);
       System.out.println("book1.memory: " + book1.memory);
       System.out.println("book1.owner : " + book1.owner);
      
       //调用book1的成员方法
       book1.type("hello world");
       book1.playMusic("<<我站在草原望北京>>");
       book1.showInfo();
      
    }
}

对象的使用:

  目前,对象基本上就是两个方面可供使用:

    > 成员变量的访问(access)

    > 成员方法的调用(invoke/call)

  

  •   成员之间是可以互相使用的!
  •   成员变量可以使用成员方法的返回值
  •   成员方法可以使用成员变量的值!

六、一个对象的内存图解:

重点:

方法区的概念

成员变量的初始化

方法区:用于存放.class字节码文件和方法定义的空间(方法并不是每个实例对象都保存一份,只是引用了方法定义的地址而已!!方法区又称为共享区!!!)

成员变量的初始化:系统的默认初始化之后,才进行显式的初始化!

七、两个对象的内存图解:

.class字节码文件只是第一次使用时加载,以后再次使用,不会多次加载!!!

不同对象有自己不同的成员变量值,但是方法都是引用方法区中的地址!!!

八、两个变量指向同一个对象图解:

重点:

多个对象名对实例对象成员变量的更改,通过其它对象名也是可以看到的.

重点:

1.方法区中存放的是程序中使用到的.class字节码文件,同一个字节码文件会在第一次使用时加载,不会多次加载.

2.不同对象中并不会保存方法的代码,只是保存了方法的地址引用而已.也就是说:方法区中的东西都是被不同的对象所共享的.方法区也称为共享区!

3.在对象的创建时,对象的成员变量首先进行默认的初始化,然后是显式的初始化(在类定义时对成员变量的赋值!).

在对象创建后,可以通过对象名.成员变量名的方式对成员变量进行更改!

Dog d = new Dog();

d.name = "abc";//如果没有显式初始化的话,name这个变量被赋值了两次:

第一次是jvm默认初始化,第二此时用对象名.变量名的方式赋值!

=============

main方法一般是单独放到一个源文件中,

为了查找类方便.合二为一啦!

把普通类定义和含main方法的测试类放到一个源文件中!!

/*
    对Phone类的测试类
    
*/

/*
    自定义手机类
    
*/
class Phone{
    //成员变量
    String brand; //null
    int price = 2000;
    
    //成员方法
    public void call(String name){
        System.out.println("给 " + name + " 打电话");
    }
    //
}

public class PhoneDemo{

    public static void main(String[] args){
        //创建对象
        Phone p = new Phone();
        
        //先对成员变量赋值
        p.brand = "iphone";
        p.price = 8000;
        System.out.println("品牌: " + p.brand + ",价格: " + p.price);
        
        //调用成员方法
        p.call("Obama");
    }
    
}
题目:以Phone p = new Phone();这条语句为例,简述在内存中都发生了什么样的变化!
  1. 在Phone对象的创建时,对象的成员变量首先进行默认的初始化,然后是显式的初始化(在类定义时对成员变量的赋值!)
  2. 在堆空间中开辟一片空间,把成员变量赋值(默认的初始化值,显示初始化)
  3. 把对象把对象的内存的空间地址值赋值给变量p
  4. main方法一般单独放到一个原文件当中,为了查找方便,把普通类定义和含main方法的测试类放到一个源文件当中
原文地址:https://www.cnblogs.com/akinodoo/p/9865275.html