java中遇到的问题

1.java中有两种数据类型:基本数据类型(8种)和对象数据类型。


2.数组是对象数据类型,只要new 了,那么肯定在内存中为数组分配了空间,也为每个数组元素分配了初始值。

     数组元素类型是基本类型中的整数类型(byte、short、int、long),则数组元素的值是0

     数组元素类型是基本类型中的浮点类型(float、double),则数组元素的值是0.0

     数组元素类型是基本类型中的字符类型(char),则数组元素的值是'\u0000'

     数组元素类型是基本类型中的布尔类型(boolean),则数组元素的值是false

     数组元素类型是基本类型中的引用类型(类、接口、数组),则数组元素的值是null

3.有序数组提高了查找速度,但降低了插入速度

4.无序数组的插入时间是常数O(1),他总是插入到数组的最后一项,array[n] = key; n++;  有序数组的插入O(N)

5.线性查找时间与N(数组长度)成正比O(N),二分查找时间是  O(logN)

6. 无序数组的删除O(N),有序数组的删除O(N)  由于平均半数的数据项为了填补“空洞”必须要移动,所以删除操作要O(N)

7. 冒泡排序:O(N2)

算法思想:

       第一趟都把最大的那个比较出来,并且放到数组最后,比较是相邻的两个两两比较,进行交换,找出最大的数据放到最后,再从头开始比较,这一次比较到数组的倒数第二个数据。也就是说

      每一趟比较的次数 = 数组的长度 - 这一次是第几次比较  (内层循环)

      那么一共要执行几趟呢?如果忘记了,可以简单的找5个数,数一下就好了,或者只要记住,比较的趟数 = 数组长度 -1;(外层循环)

      int[] a = new int[]{2,4,1,0,9,5,8,7,3,6};
      for (int i = 0; i < a.length-1; i++) {
            //每一次都从头开始排序
            for (int j = 0; j < a.length- i-1; j++) {
                if (a[j] < a[j+1]) {
                    int temp ;
                    temp = a[j];
                    a[j] =a[j+1];
                    a[j+1] = temp;
                }
            }
        }
8.选择排序:O(N2)
算法思想:

       先在笔记本上把第0个数据和下标记录下来,从第一个位置开始,把所有数据扫描一趟,这期间每个数据都要与笔记本上记录的数据进行比较,如果有比笔记本上记录的数据小的,那么把笔记本上原来的那个划掉,把这个新的值和下标都添加上去,这样一趟下来,就能从中找出最小的那个值和下标,然后把这个最小的值和第0个位置进行交换,这样左侧第一个数据就是有序的了。

       第二次扫描,笔记本上记录1号位置的数据和下标,从2号位置开始扫描比较,这一趟下来,把找到的最小值和1号位置的数据进行交换,这样左侧的0号1号数据就都有序了。

       第三次扫描,笔记本上记录2号位置的数据和下标,从3号位置开始扫描比较,这一趟下来,把找到的最小值和2号位置的数据进行交换,这样左侧的0号1号2号数据就都有序了。

      以此类推。。。N个数据进行N-1趟扫描(外层循环)  每一趟扫描,都是从开始笔记本记录的值的下一个位置开始比较,一直比较到最后(内层循环)

        int[] a = new int[] { 2, 4, 1, 0, 9, 5, 8, 7, 3, 6 };
        int min, target ;
        for (int i = 0; i < a.length - 1; i++) {
            min=a[i];target = i;
            for (int j = i+1; j < a.length; j++) {
                if (min > a[j]) {
                     min = a[j];
                    target = j;
                }
            }
            // 一趟比较之后,开始把最小的移动到做左端
            if (target != i) {
                int temp;
                temp = a[target];
                a[target] = a[i];
                a[i] = temp;
            }
        }

选择排序和冒泡排序时间复杂度都是O(N2),比较的次数是相同的,但是选择排序的交换次数更少,所以选择排序比比较排序快一些。

9.插入排序:O(N2)

算法思想:

       第0号数据本身是有序的,外层循环out变量从下标1开始,向右移动,他标记了未排序部分最左端的数据,temp记录每一趟外层循环最开始的数据,内层循环while中,in变量从out变量开始,向左扫描,如果in左侧有合适插入temp的位置,把temp这个位置空出来,把in左侧数据中比temp数据大的向右移动,然后记录temp所要插入的位置,在内层循环这一趟结束以后,把temp插入到相应的位置。while循环的每一趟都向右移动了一个已排序的数据项。插入排序对基本有序的数组效率很高。

        int[] a = new int[] { 2, 4, 1, 0, 9, 5, 8, 7, 3, 6 };
        int in,out;
        for (out = 1; out < a.length; out++) {

            //每一趟外层循环结束,out及其左侧数据都是有序的

            int temp = a[out];
            in = out;
            while (in >0 && a[in -1] >= temp) {
                a[in] = a[in-1];
                in--;
            }
            a[in] = temp;
        }

10 栈和队列是作为程序员的辅助工具来运用,和数组这种用来数据存储的工具不一样,在程序操作执行期间他们被创建,通常用他们去执行某项特殊的任务,完成后就销毁。

11.栈

 push方法 先将top值加一,然后再插入数据项

  public void push(int i){

        top++;

        stackArray[top] = i;

  }

 pop方法  先将数据项移除,然后top--

 public int pop(){

      return stackArray[top--];

  }

peek 方法只能查看栈顶元素,不对栈进行任何修改

12. 在抢先式多任务操作系统中,程序排列在优先级队列中,这样优先级最高的程序就会先得到时间片并得以运行。

13. java中创建一个对象必须要用new

14. system.out.flush  表示强制清空缓冲区,并将消息立即送出。输入输出流一般都是用缓冲的,也就是说,不是每次你要它读/写一个或少数几个字节,他就立马执行的,而是  

      将这些请求放入缓冲,积累到一定量的时候才一起执行,以提高效率。flush的作用就是强制将缓冲区里的东西立刻执行然后清空,不管里面有多少东西。

     在System.out中,此输出流是将数据输出到屏幕上,因此,速度很快,不需要缓冲,因此,用或不用System.out.flush() 没有什么区别。但如果另外一个输出流是把数据输送

     到网络上另外一台机器上,传输受到网络的限制,尚未发送的数据被存在缓冲区里,这时你调用flush()就可以确保缓冲区的数据全部被发送后再执行后面的语句。

15. 递归必须要有基值条件。递归函数用栈存储函数返回值和中间过程,每次返回栈顶结果(包括中间的输出值和返回值),而递归函数最终的返回值是栈底的值,也就是递归

      函数第一次调用的返回值。

16. 归并排序O(N*logN)归并排序需要一个和原数组大于等于的临时数组

      非递归方法就是,第一次01,23,45,67;第二次是0123,4567;第三次是01234567;注意数组越界问题。

      递归排序方法是,将数组a分成左右两部分,再将左右两部分再分成左右两部分,基值条件是:low == high,也就是划分到单个值的时候停止;

       归并排序空间效率低一些。归并排序比简单排序要快,但是他需要的空间是原始数组空间的两倍。

17. 希尔排序O(N *(logN)2)

      希尔排序基于插入排序,通过加大元素之间的间隔(插入排序元素间隔是1),希尔排序中有一个间隔序列,在这些有间隔的元素中进行插入排序,从而使数据项能够大跨度的移动。当这些数据项排过一趟序之后,希尔排序算法减小数据项的间隔再进行排序。

      比如:使用间隔序列为h=3*h-1,假设数组中有10个数,第一趟:h=4,(0,4,8)  (1,5,9)   (2,6)   (3,7)   共四组,这四组中每个组的组内排序是用插入排序,这一趟结束以后在相应下标位置上的数字就是有序的了;第二趟:h=1,这一次就和插入排序一样了,元素间隔是1,但是由于已经基本有序,所以移动次数减少,效率提高。

      间隔序列有很多种,书上只举了一种。间隔序列是正着生成倒着使用的,无论那种间隔序列,最后一次间隔都是1。

       //间隔序列
        int h = 1;
        while (h <= (a.length -1)/3) {
            h = 3*h+1;
        }
        while (h>0) {
            //插入排序
            int in,out,temp;
             for (out = h; out < a.length; out++) {
                in = out;
                temp = a[out];
                while (in>=h && a[in-h] >= temp) {
                    a[in] = a[in-h];
                    in-=h;
                }
                a[in] = temp;
            }
            
            h = (h-1)/3;
        }

18. 快速排序O(N*logN)

      算法思想:通过把一个数组划分为两个子数组,然后递归的调用自身为每一个子数组进行快速排序来实现。算法必须要使用枢纽,以及小的划分区域,这个枢纽很重要。

第一种方法:  
      选择数组最右端数据项作为枢纽

      第一次:枢纽选择为数组的最后一个值right= a.length -1,以这个枢纽为划分依据,使得左边子数组都比枢纽小,右边子数组都比枢纽大,分割边界的下标数值指向右边数组的最左侧的数据项。将这个数据项和枢纽a[right]交换,这样,这个枢纽所在的位置的值就有序了,也是他的最终位置,左右字数组之间。

      第二次:再对左右子数组进行分割,先进行左边子数组处理,right = partition -1;partition就是刚刚划分函数返回的上一个枢纽的最终位置,这一次的枢纽选择还是当前子数组的最右侧数据项,pivot = a[right];pivot是枢纽的值。再重复上一次的划分方法。

      。。。。。。

     
      每一层递归,左边的数组总比右边的数组先处理。基值条件是如果left>=right就返回;

private static void recquick(int[] a, int left, int right) {
        if (left >= right) {
            return;
        }else {
            int pivot = a[right];
            int partition = partitionIt(a,left,right,pivot);
            recquick(a, left, partition-1);
            recquick(a, partition+1, right);
        }
    }

//划分的方法,pivot永远都是子数组最右边那个值

private static int partitionIt(int[] a, int left, int right,int pivot) {
        int leftPtr = left-1;
        int rightPtr = right;
        while (true) {
            while (a[++leftPtr] < pivot);
            while (rightPtr>left && a[--rightPtr]>pivot);
            if (leftPtr >= rightPtr) {
                break;
            }else {
                //交换
                swap(a, leftPtr, rightPtr);
            }
        }
        swap(a, right, leftPtr);
        return leftPtr;
    }


//交换

private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

 

第二种方法:

       中值划分法,把数组左边,中间,右边三个值排序,排好序之后,把中间的值和数组的右边倒数第二个值交换,这样就把数组的a[right-1]作为枢纽了,划分的时候左边从数组的第二个值开始遍历,右边从数组的倒数第三个值开始遍历,因为在中值划分的时候,左边比枢纽小,右边的值比枢纽大,所以他们已经在正确的划分中了。其他的和第一种方法相似,但是这个方法要求数组长度小于等于3时,直接进行交换排序,这也是基值条件。size = right - left + 1;


       选择了中间项作为枢纽,但是,他在三个值排序的函数中,还把中间值和数组最右端倒数第二个值进行交换了,也就是说这个中间值,最终跑到了数组倒数第二个位置上了。为什么会选择倒数第二个位置呢?因为倒数第一个位置的值已经在划分范围内了。

第三种方法:数组很小的时候用插入排序,数组很大的时候再用快速排序。

19.堆排序

构建大根堆:将array看成完全二叉树的顺序存储结构

private int[] buildMaxHeap(int[] array){
        //从最后一个节点array.length-1的父节点(array.length-1-1)/2开始,直到根节点0,反复调整堆
        for(int i=(array.length-2)/2;i>=0;i--){
            adjustDownToUp(array, i,array.length);
        }
        return array;
 }

    //②将元素array[k]自下往上逐步调整树形结构
    private void adjustDownToUp(int[] array,int k,int length){
        int temp = array[k];   
        for(int i=2*k+1; i<length-1; i=2*i+1){    //i为初始化为节点k的左孩子,沿节点较大的子节点向下调整
            if(i<length && array[i]<array[i+1]){  //取节点较大的子节点的下标
                i++;   //如果节点的右孩子>左孩子,则取右孩子节点的下标
            }
            if(temp>=array[i]){  //根节点 >=左右子女中关键字较大者,调整结束
                break;
            }else{   //根节点 <左右子女中关键字较大者
                array[k] = array[i];  //将左右子结点中较大值array[i]调整到双亲节点上
                k = i; //【关键】修改k值,以便继续向下调整
            }
        }
        array[k] = temp;  //被调整的结点的值放人最终位置
    }

    //③堆排序
    public int[] heapSort(int[] array){
        array = buildMaxHeap(array); //初始建堆,array[0]为第一趟值最大的元素
        for(int i=array.length-1;i>1;i--){  
            int temp = array[0];  //将堆顶元素和堆低元素交换,即得到当前最大元素正确的排序位置
            array[0] = array[i];
            array[i] = temp;
            adjustDownToUp(array, 0,i);  //整理,将剩余的元素整理成堆
        }
        return array;
    }

    //删除堆顶元素操作
    public int[] deleteMax(int[] array){
        //将堆的最后一个元素与堆顶元素交换,堆底元素值设为-99999
        array[0] = array[array.length-1];
        array[array.length-1] = -99999;
        //对此时的根节点进行向下调整
        adjustDownToUp(array, 0, array.length);
        return array;
    }

    //插入操作:向大根堆array中插入数据data
    public int[] insertData(int[] array, int data){
        array[array.length-1] = data; //将新节点放在堆的末端
        int k = array.length-1;  //需要调整的节点
        int parent = (k-1)/2;    //双亲节点
        while(parent >=0 && data>array[parent]){
            array[k] = array[parent];  //双亲节点下调
            k = parent;
            if(parent != 0){
                parent = (parent-1)/2;  //继续向上比较
            }else{  //根节点已调整完毕,跳出循环
                break;
            }
        }
        array[k] = data;  //将插入的结点放到正确的位置
        return array;
    }

19.substring用法:

public String substring(int beginIndex,int endIndex)返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。

String str = "abcdefg";

String s = str.substring(0,2);------>输出是ab

 

20 "=="和equals()

        “==”习惯用于基本类型之间的比较,而equals()仅用于对象之间的比较。“==”对比两个对象基于内存引用,如果两个对象的引用完全相同(指向同一个对象,内存地址相同)时,“==”操作将返回true,否则返回false。

        ==与equals的主要区别是:==常用于比较原生类型而equals()方法用于检查对象的相等性。另一个不同的点是:如果==和equals()用于比较对象,当两个引用地址相同,==返回true。而equals()可以返回true或者false主要取决于重写实现。

字符串的==和equals对比

因为java.lang.String类重写了equals方法,如果两个字符串对象包含有相同的内容它返回true,但是==只有他们的引用地址相同时才返回true。字符串的对比使用equals()代替==操作符。

 

总结:

1.如果是基本类型之间的比较,就用“==”。

2.如果是对象之间的比较,如果想知道对象的内容是否相同,用equals(),并且要重写equals()方法,如果不重写,equals()方法默认和==是一样的,都是返回的内存地址是否相同。

3.字符串:如果想要看这两个字符串是不是同一个对象,即想知道这两个字符串内存地址是否指向同一个地方,用==;如果想要比较两个字符串内容是不是相同,用equals(),因为字符串里面重写了equals()方法。一般字符串都是比较的内容是否相同,所以都用equals。

 

21.通过参数传递过来的对象叫依赖,通过方法得到的对象叫关联。

 

22.域对象:servletcontext,request,session;他们都有void setAttribute(String name,Object value);Object getAttribute(String name);void removeAttribute(String name);这三个方法。

23. 服务器只解析动态java代码:servlet,jsp,客户端解析静态代码:html,js等等。

24.servlet主要干这些工作:获取表单数据,处理业务逻辑,分发转向。

25.javascript实现全选和反选功能:见bookmanager项目的admin/products/list.jsp

function checkAll(){
		//得到ckAll元素,并得到它的选中状态
		var flag = document.getElementById("ckAll").checked;
		//得到所有ids复选框元素
		var ids = document.getElementsByName("ids");
		//循环给每个复选框赋值
		for(var i = 0;i<ids.length;i++){
			ids[i].checked = flag;
		}
	}

26. servletContext对象整个web project就只有一个,服务器已启动,servletContext对象就创建了。服务器一启动,先扫描web.xml。

27. 数据库中的limit,一般用于分页查询

 int currentpage = 1;//当前页

 int pagesize=3;//每页显示条数

 select * from user limit  (currentpage-1)*pagesize, pagesize;//0,1,2   3,4,5   6,7,8

 第一个参数代表从第几行开始查询,第二个参数代表每次查询几行

28. 主表和子表区分,就看先有的是哪张表,子表要引用,参照主表,所以子表要有外键。比如学生表和成绩表,先有的是学生表,成绩表的学号就是外键,但是,注意,学号在学生表必须是主键,这样学号在成绩表才能是外键,否则不可能创建外键。并且外键和主键的类型要一致。在成绩表加了外键,这两个表就有了联系。

29.建立主外键关联的目的是为了避免插入数据,删除数据,修改数据错误,和查询没有任何关系。查询只看表之间是否有匹配的

30.过滤器是服务器一启动就执行初始化。

31.转发是服务器行为,重定向是浏览器行为。请求域中的数据转发不丢失,重定向丢失。转发地址栏变,重定向地址栏不变。转发是一次请求,重定向是两次请求。

32.javascript dom
window.onload = function(){} 表示等页面的所有元素加载完毕,并且所有要请求的资源也加载完毕,才会出发function函数里的代码。

33.request.getContextPath  相当于   /    也就是   /examsystem

34. DBCP用的是动态代理,C3P0用的是静态代理。

35.javaweb 整个开发过程中表单验证分为两种,客户端验证和服务端验证,客户端验证就是通过javascript dom 进行表单验证,任何时候,服务端验证都是必不可少的。因为更安全。 我们在实际开发中采用的是二者都用。

36. String s1 = "";
      StringUtils.isEmpty(s1)-----true
      String s2 = null;
      StringUtils.isEmpty(s2) ----true
      String s3 = "    "
      StringUtils.isEmpty(s3)----false

isEmpty()验证字符串是否为null,或者是否是空字符串,但是不去空格
isBlank() 验证字符串是否为null,或者是否是空字符串,但是去空格。

37. 不同的操作系统支持不同的CPU指令集。 平台= CPU+OS  java跨平台原理:点击打开链接
JDK工具包包含编译java源码的编译器javac

38. Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。

39.java的跨平台:
 
java源程序先经过javac编译器编译成二进制的.class字节码文件(java的跨平台指的就是.class字节码文件的跨平台,.class字节码文件是与平台无关的),.class文件再运行在jvm上,java解释器(jvm的一部分)会将其解释成对应平台的机器码执行,所以java所谓的跨平台就是在不同平台上安装了不同的jvm,而在不同平台上生成的.class文件都是一样的,而.class文件再由对应平台的jvm解释成对应平台的机器码执行

40.JRE java Runtime Environment   java运行时环境  JRE包含JVM和需要运行的程序。
  JDK java development kit     java开发工具包  JDK包含了JRE,编译器等
  JDT java development tooling   eclipse提供的一组API
 
只有JRE只能进行程序编译,不能进行java程序的开发。安装JDK才能正常进行java程序的开发,编译,运行。

41.<input type="button" value="btn" οnclick="fn(this)">
 
  <script>
    function fn(obj){
       alert(obj.type);//button
       alert(obj.value);//btn
    }

 </script>
this--经过事件的函数传递的是html标签对象

42.二分查找:

private static int binaryFind(int[] data, int target) {
		int start = 0;
		int end = data.length-1;
		int mid = (start+end)/2;
		while(start<=end){
			if (target == data[mid]) {
				return mid;
			}else if (target<data[mid]) {
				end = mid-1;
				mid = (start+end)/2;
			}else {
				start = mid+1;
				mid = (start+end)/2;
			}
		}
		return -1;
	}start<=end){
			if (target == data[mid]) {
				return mid;
			}else if (target<data[mid]) {
				end = mid-1;
				mid = (start+end)/2;
			}else {
				start = mid+1;
				mid = (start+end)/2;
			}
		}
		return -1;
	}


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值