首页 文章 精选 留言 我的

精选列表

搜索[整理],共9265篇文章
优秀的个人博客,低调大师

ONNX整理

ONNX(Open Neural Network Exchange)——开放神经网络交换格式,作为框架共用的一种模型交换格式,使用protobuf二进制格式来序列化模型(protobuf序列化可以参考Netty整合Protobuffer ),可以提供更好的传输性能。官方github:GitHub - onnx/onnx at f2daca5e9b9315a2034da61c662d2a7ac28a9488 ONNX将每一个网络的每一层或者说是每一个算子当作节点Node,再由这些Node去构建一个Graph,相当于是一个网络。最后将Graph和这个onnx模型的其他信息结合在一起,生成一个model,也就是最终的onnx模型。实例如下 创建ONNX模型 创建onnx模型有两种方法,一种是其他框架转换过来,如Pytorch、PaddlePaddle等,从Pytorch转换onnx可以参考模型部署篇 的Pytorch 权重 pth 转换 onnx;PaddlePaddle转换onnx可以参考PaddleOCR使用指南 中的Paddle2ONNX。 我们先来生成一个onnx文件 import torch import torch.nn as nn from torch.autograd import Variable class Network(nn.Module): def __init__(self): super(Network, self).__init__() self.conv = nn.Conv2d(1, 1, 1) self.act = nn.ReLU() def forward(self, x): return self.act(self.conv(x)) if __name__ == '__main__': net = Network() input = Variable(torch.randn([1, 1, 1, 1])) torch.onnx.export(net, input, 'net.onnx', opset_version=10) 然后来打印这个onnx文件的结构 import torch import torch.nn as nn from torch.autograd import Variable import onnx class Network(nn.Module): def __init__(self): super(Network, self).__init__() self.conv = nn.Conv2d(1, 1, 1) self.act = nn.ReLU() def forward(self, x): return self.act(self.conv(x)) if __name__ == '__main__': # net = Network() # input = Variable(torch.randn([1, 1, 1, 1])) # torch.onnx.export(net, input, 'net.onnx', opset_version=10) print(onnx.load("./net.onnx")) 运行结果 ir_version: 5 producer_name: "pytorch" producer_version: "1.12.1" graph { node { input: "input.1" input: "conv.weight" input: "conv.bias" output: "input" name: "Conv_0" op_type: "Conv" attribute { name: "dilations" ints: 1 ints: 1 type: INTS } attribute { name: "group" i: 1 type: INT } attribute { name: "kernel_shape" ints: 1 ints: 1 type: INTS } attribute { name: "pads" ints: 0 ints: 0 ints: 0 ints: 0 type: INTS } attribute { name: "strides" ints: 1 ints: 1 type: INTS } } node { input: "input" output: "4" name: "Relu_1" op_type: "Relu" } name: "torch_jit" initializer { dims: 1 dims: 1 dims: 1 dims: 1 data_type: 1 name: "conv.weight" raw_data: "\014\317B?" } initializer { dims: 1 data_type: 1 name: "conv.bias" raw_data: "\344\n\026\277" } input { name: "input.1" type { tensor_type { elem_type: 1 shape { dim { dim_value: 1 } dim { dim_value: 1 } dim { dim_value: 1 } dim { dim_value: 1 } } } } } output { name: "4" type { tensor_type { elem_type: 1 shape { dim { dim_value: 1 } dim { dim_value: 1 } dim { dim_value: 1 } dim { dim_value: 1 } } } } } } opset_import { version: 10 } 首先是onnx版本,我们这里为ir_version: 5,然后是从什么框架转换过来的,这里是从Pytorch转换过来的producer_name: "pytorch",版本号是producer_version: "1.12.1"。 然后是graph->node,第一个node是2D卷积核,第二个node是Relu激活函数。node中的op_type是节点类型,所有类型可以参考https://github.com/onnx/onnx/blob/f2daca5e9b9315a2034da61c662d2a7ac28a9488/docs/Operators.md。name是节点名称,它跟op_type是不同的。attribute是节点属性,在Conv_0中就是2D卷积的各种属性,比如"group"是分组卷积,"kernel_shape"是卷积核尺寸等等。initializer是初始化,包含了权重初始化和偏置初始化。input是输入,包含输入的形状,output是输出,包含输出的形状。opset_import为当前的模型文件所依赖的算子domain和版本。 最后我们来检查该模型,运行是没有问题的。 import torch import torch.nn as nn from torch.autograd import Variable import onnx class Network(nn.Module): def __init__(self): super(Network, self).__init__() self.conv = nn.Conv2d(1, 1, 1) self.act = nn.ReLU() def forward(self, x): return self.act(self.conv(x)) if __name__ == '__main__': # net = Network() # input = Variable(torch.randn([1, 1, 1, 1])) # torch.onnx.export(net, input, 'net.onnx', opset_version=10) # print(onnx.load("./net.onnx")) model = onnx.load("./net.onnx") onnx.checker.check_model(model) 另外一种就是用onnx自己的方法创建onnx模型。 import onnx import onnx.helper as helper import numpy as np if __name__ == '__main__': input = helper.make_tensor_value_info(name='input', elem_type=onnx.TensorProto.FLOAT, shape=[1, 3, 244, 244]) output = helper.make_tensor_value_info(name='output', elem_type=onnx.TensorProto.FLOAT, shape=[1, 3, 244, 244]) weight = helper.make_tensor(name='weight', data_type=onnx.TensorProto.FLOAT, dims=[3, 3, 1, 1], vals=np.random.randn(3, 3, 1, 1)) bias = helper.make_tensor(name='bias', data_type=onnx.TensorProto.FLOAT, dims=[3], vals=np.random.randn(3)) node = helper.make_node(op_type='Conv', inputs=['input', 'weight', 'bias'], outputs=['output'], kernel_shape=[1, 1], strides=[1, 1], group=1, pads=[0, 0, 0, 0]) graph = helper.make_graph(name='graph', nodes=[node], inputs=[input], outputs=[output], initializer=[weight, bias]) model = helper.make_model(graph) onnx.checker.check_model(model) print(model) onnx.save_model(model, 'model.onnx') 运行结果 ir_version: 8 graph { node { input: "input" input: "weight" input: "bias" output: "output" op_type: "Conv" attribute { name: "group" i: 1 type: INT } attribute { name: "kernel_shape" ints: 1 ints: 1 type: INTS } attribute { name: "pads" ints: 0 ints: 0 ints: 0 ints: 0 type: INTS } attribute { name: "strides" ints: 1 ints: 1 type: INTS } } name: "graph" initializer { dims: 3 dims: 3 dims: 1 dims: 1 data_type: 1 float_data: 0.45837152004241943 float_data: 0.10209446400403976 float_data: 1.0382566452026367 float_data: -0.09292714297771454 float_data: 1.58871591091156 float_data: 0.3746287226676941 float_data: -0.35588690638542175 float_data: 0.7165427207946777 float_data: 0.10244251787662506 name: "weight" } initializer { dims: 3 data_type: 1 float_data: -0.36782845854759216 float_data: 2.305680513381958 float_data: -0.13051341474056244 name: "bias" } input { name: "input" type { tensor_type { elem_type: 1 shape { dim { dim_value: 1 } dim { dim_value: 3 } dim { dim_value: 244 } dim { dim_value: 244 } } } } } output { name: "output" type { tensor_type { elem_type: 1 shape { dim { dim_value: 1 } dim { dim_value: 3 } dim { dim_value: 244 } dim { dim_value: 244 } } } } } } opset_import { version: 17 } 动态设置batch_size 在上面的结果中,我们可以看到input的维度都是固定值[1,3,244,244],现在我们要来改变这个固定值为可以动态输入的值。我们先将模型给运行起来。 import onnx import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load("./model.onnx") sess = onnxruntime.InferenceSession('./model.onnx') input = np.random.randn(1, 3, 244, 244).astype(np.float32) print(sess.run(['output'], {'input': input})) 运行结果 [array([[[[-7.4062514e-01, 2.5951520e-01, -3.5876265e-01, ..., -2.0852795e+00, -1.0078001e-01, -4.9386564e-01], [-6.0379845e-01, 9.2830718e-01, -4.2096943e-02, ..., -1.9139317e-01, 1.6547061e+00, 1.4468774e+00], [ 2.6494553e+00, -9.6209788e-01, 8.2099646e-02, ..., -1.5899204e+00, -1.3295431e+00, 1.1512205e-01], ..., [ 1.4135087e+00, 6.4077592e-01, -5.6514746e-01, ..., 2.1367333e+00, 2.6012421e+00, -1.3565271e+00], [ 6.9879985e-01, 1.2454928e+00, 6.0045028e-01, ..., -6.1302024e-01, -4.3026954e-02, -7.2975445e-01], [-2.1020520e+00, -1.2499222e+00, -9.3896770e-01, ..., -4.6129468e-01, 5.4580927e-01, -7.4599540e-01]], [[ 5.6230574e+00, 2.6218858e+00, 7.1071947e-01, ..., 3.6510468e-02, 2.5771899e+00, 2.0060635e+00], [ 4.2759910e+00, 2.5261867e+00, 1.0787441e+00, ..., 3.3373690e+00, 4.5090003e+00, 3.5535808e+00], [ 1.6522924e+00, 1.5206050e+00, 3.6905313e+00, ..., 1.5963824e+00, 5.1875353e-02, 3.4248161e+00], ..., [ 1.0295208e+00, 4.5397396e+00, 4.3366423e+00, ..., 1.2408195e+00, 3.1239326e+00, 1.7476916e+00], [ 9.7080982e-01, 1.9692242e+00, 3.7690439e+00, ..., -1.6770840e-01, 1.1871569e+00, 4.2690439e+00], [ 4.4730301e+00, 1.5573008e+00, 7.2707558e+00, ..., 4.7898588e+00, 2.9080591e+00, 7.2294927e-01]], [[ 1.3509388e+00, -1.9160898e-01, -1.3318433e+00, ..., -1.0562456e+00, 1.0652192e-01, -4.4993240e-01], [ 7.3106253e-01, -4.0714890e-03, -5.3625894e-01, ..., -6.2385768e-02, 3.3464909e-01, 2.7667671e-01], [-7.8517151e-01, -7.1918708e-01, 5.5366117e-01, ..., -4.7982591e-01, -1.0322813e+00, 8.0901492e-01], ..., [-1.0904443e+00, 4.7577775e-01, 9.5288980e-01, ..., -9.8435390e-01, -5.1632053e-01, 2.4581529e-01], [-6.4627886e-01, -9.8449951e-01, 1.6146483e-01, ..., -1.2009792e+00, -7.3006052e-01, 7.0891309e-01], [ 1.3855783e+00, -8.9338100e-01, 2.4704218e+00, ..., 6.8950468e-01, 1.7709453e-01, -7.6678610e-01]]]], dtype=float32)] 现在我们来把输入的batch_size调整成2 import onnx import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load("./model.onnx") inputs = model.graph.input outputs = model.graph.output for i in inputs: i.type.tensor_type.shape.dim[0].dim_value = 2 for o in outputs: o.type.tensor_type.shape.dim[0].dim_value = 2 onnx.checker.check_model(model) onnx.save_model(model, 'dynamic_model.onnx') sess = onnxruntime.InferenceSession('./dynamic_model.onnx') input = np.random.randn(2, 3, 244, 244).astype(np.float32) print(sess.run(['output'], {'input': input})) 运行结果 [array([[[[-2.10871696e-02, -1.32871771e+00, -1.22335061e-01, ..., 4.77721721e-01, -4.10815179e-01, -1.37511027e+00], [-1.09181249e+00, -2.02204657e+00, 1.54176390e+00, ..., -1.88722742e+00, -2.00726366e+00, 4.24929589e-01], [-7.14685619e-01, 3.82802397e-01, -2.30412316e+00, ..., 7.06834435e-01, -2.36892438e+00, -2.11947155e+00], ..., [-9.51929450e-01, -1.22408187e+00, -1.35213524e-01, ..., 5.55669367e-02, -5.95110297e-01, -2.15206313e+00], [ 8.90325904e-01, -1.89442956e+00, 8.34725618e-01, ..., -2.34860206e+00, -1.09965193e+00, -4.96994108e-01], [ 1.56639183e+00, 5.97145438e-01, -5.28750658e-01, ..., 5.77995658e-01, -1.46205699e+00, 2.80693078e+00]], [[ 3.09728765e+00, -1.42589498e+00, 7.58970022e-01, ..., 3.48910093e+00, 2.95971513e+00, 1.96736765e+00], [ 2.76622701e+00, 1.58350587e+00, 2.41761374e+00, ..., 3.68322372e-01, 3.05963039e-01, 2.99718475e+00], [-1.75151324e+00, 2.79870439e+00, -3.03543806e-01, ..., 2.86027908e+00, 1.78771615e+00, 4.79569674e+00], ..., [ 1.30739605e+00, 1.83714139e+00, 4.55001736e+00, ..., 1.44066858e+00, 4.87037659e+00, 2.10291076e+00], [ 9.44083452e-01, -8.11131001e-02, 2.89160919e+00, ..., 2.34788847e+00, 1.95467031e+00, 3.87145948e+00], [ 2.71238947e+00, 1.46723819e+00, 7.61192560e-01, ..., 2.69581342e+00, 2.11386037e+00, 4.08577728e+00]], [[ 3.52043629e-01, -1.83945060e+00, -9.97831583e-01, ..., -2.60245442e-01, 3.69277894e-01, 1.17505208e-01], [ 2.62015522e-01, -6.50106370e-01, -7.36498535e-01, ..., -3.72626394e-01, -9.92001474e-01, 1.87904552e-01], [-2.00427341e+00, -2.67415404e-01, -1.00334084e+00, ..., 8.22970718e-02, 1.41485706e-01, 1.49001801e+00], ..., [-9.85595703e-01, -9.74879414e-03, 1.27501774e+00, ..., -7.10564435e-01, 1.17551017e+00, -4.15902734e-01], [-9.80473995e-01, -1.07735765e+00, 2.39617974e-02, ..., -1.93872005e-02, 2.48361230e-02, 7.19040394e-01], [-6.61614537e-02, -4.85614896e-01, -7.31452227e-01, ..., -9.65917259e-02, -2.94267178e-01, 1.87805906e-01]]], [[[-1.05941308e+00, 8.10959578e-01, -9.29054856e-01, ..., -1.33419132e+00, -5.62950134e-01, 3.15277368e-01], [-2.45844007e+00, -5.31174302e-01, 8.06264520e-01, ..., -1.37343729e+00, -1.26287377e+00, -1.79255664e+00], [ 5.01155496e-01, 2.53203034e+00, -9.11398768e-01, ..., -2.61194611e+00, -6.27550602e-01, -1.04612875e+00], ..., [ 5.64767838e-01, 1.82380235e+00, -9.87865806e-01, ..., -1.48546624e+00, 5.00284791e-01, -1.14099467e+00], [-1.48488015e-01, -3.75306606e-03, 2.05217457e+00, ..., -4.82964367e-01, 6.37757182e-01, 5.87742925e-01], [-7.62285709e-01, 5.78535438e-01, -9.07517672e-01, ..., -1.40203249e+00, 3.13063234e-01, 9.46564317e-01]], [[ 2.21778965e+00, 1.17825162e+00, 1.17773283e+00, ..., 4.21785736e+00, 1.93207061e+00, 6.90674305e+00], [ 5.16840172e+00, 4.03573513e-02, 3.72957373e+00, ..., 2.57324958e+00, 3.23857665e-01, 8.98278236e-01], [ 1.18916261e+00, 4.03137350e+00, 1.54717636e+00, ..., 5.73142242e+00, 2.54209590e+00, 3.02691102e+00], ..., [ 2.02949071e+00, 4.00444984e+00, 3.55739307e+00, ..., 5.54533482e-01, 3.57894540e+00, 7.03547835e-01], [ 2.57975435e+00, 2.32062602e+00, 4.18669128e+00, ..., 2.15663671e+00, 2.39567637e+00, 7.93485880e-01], [ 3.32399893e+00, 3.12817383e+00, 3.60134292e+00, ..., 1.70791423e+00, 7.71586776e-01, 3.58140349e+00]], [[-6.90246701e-01, -8.55753422e-01, -1.35433823e-01, ..., 9.99482393e-01, -2.96287388e-01, 2.49611807e+00], [ 1.56937921e+00, -9.95752215e-01, 5.38442284e-02, ..., 1.63274094e-01, -9.27845955e-01, -6.64922059e-01], [-5.40241778e-01, 2.26666585e-01, -2.95405626e-01, ..., 1.90356636e+00, 4.94795978e-01, 1.35599896e-01], ..., [-4.09579694e-01, 1.26961544e-01, 5.97525239e-01, ..., -9.00853217e-01, 8.11160445e-01, -8.88532698e-01], [-5.75763881e-01, -1.15364529e-01, 2.42510274e-01, ..., 1.83168098e-01, -3.83193374e-01, -1.10992551e+00], [ 9.75027233e-02, 1.07848495e-02, 2.93477297e-01, ..., -2.67393768e-01, -8.09366763e-01, 6.60410523e-03]]]], dtype=float32)] 但是现在batch_size依然是一个固定值,如果我们修改input的第一个维度,是会报错的。则我们需要修改成以下的方式才能输入任意的batch_size。 import onnx import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load("./model.onnx") inputs = model.graph.input outputs = model.graph.output for i in inputs: i.type.tensor_type.shape.dim[0].dim_param = 'batchsize' for o in outputs: o.type.tensor_type.shape.dim[0].dim_param = 'batchsize' onnx.checker.check_model(model) onnx.save_model(model, 'dynamic_model.onnx') sess = onnxruntime.InferenceSession('./dynamic_model.onnx') input = np.random.randn(3, 3, 244, 244).astype(np.float32) print(sess.run(['output'], {'input': input})) 运行结果 [array([[[[-5.6308472e-01, -2.8269453e+00, -2.7103744e+00, ..., 4.2550400e-01, 6.5147376e-01, -4.7779888e-02], [-2.5536952e+00, 1.1469245e-01, 3.4514198e-01, ..., -1.8919052e+00, -5.7445437e-01, -1.5864235e+00], [-1.7443299e-02, -8.9739335e-01, -2.9766396e-01, ..., 2.7872375e-01, -8.8234627e-01, -2.3681331e+00], ..., [-1.3148707e+00, -5.4888296e-01, 4.1061863e-01, ..., -1.0763314e+00, -9.6379507e-01, 1.3077673e+00], [-6.3514382e-02, -5.1493609e-01, -1.5793841e+00, ..., -2.2589236e-02, -2.2170777e+00, 1.2437304e+00], [ 7.4394345e-01, 7.8581774e-01, 2.0062235e-01, ..., -1.4014708e+00, 5.5377036e-02, 3.6608991e-01]], [[ 5.4129419e+00, 1.7448205e+00, 3.4165416e+00, ..., 1.0320716e+00, 1.6988618e+00, 5.1501741e+00], [-1.3918903e+00, 1.7199724e+00, 2.1343894e+00, ..., 8.0553353e-01, 4.7985373e+00, 2.5783958e+00], [ 2.3555427e+00, 6.3222194e-01, 2.9314611e+00, ..., 4.3459427e-01, 1.3417060e+00, 1.6852837e+00], ..., [ 5.7537341e-01, 3.0654173e+00, -5.7629395e-01, ..., 1.0968879e+00, 3.7861698e+00, 1.4928346e+00], [ 3.1267416e+00, 2.0358701e+00, 2.2204084e+00, ..., 5.2084265e+00, 3.9166064e+00, 6.4575119e+00], [-7.2486067e-01, 2.3311584e+00, 2.0912974e+00, ..., 1.8693907e+00, 3.2796674e+00, 3.8991761e+00]], [[ 1.1898929e+00, 8.9648962e-03, 8.5148907e-01, ..., -4.7057205e-01, -7.9108685e-01, 1.0573645e+00], [-1.5732453e+00, -3.8554335e-01, -1.6086581e-01, ..., -8.1125468e-01, 1.2085729e+00, 5.6812420e-02], [-3.0767348e-01, -8.5083431e-01, 4.9003422e-02, ..., -7.8210533e-01, -5.2408022e-01, -2.3199841e-02], ..., [-7.3540843e-01, -2.9384446e-01, -1.6465921e+00, ..., -3.8980949e-01, 7.1137357e-01, -8.0783540e-01], [ 2.3953258e-01, -1.7050017e-01, -1.3933203e-01, ..., 1.6591790e+00, 1.0759927e+00, 1.7683787e+00], [-1.6956003e+00, -4.6602386e-01, -3.4259117e-01, ..., -1.0014131e-01, 2.6990986e-01, 8.6363363e-01]]], [[[ 6.4478827e-01, -8.1067204e-01, -1.2237258e+00, ..., -1.2951733e+00, -6.2070227e-01, -1.2906476e+00], [-6.6038930e-01, -2.8674665e-01, -1.0612940e+00, ..., 4.6769258e-01, 4.8500946e-01, -5.6188315e-01], [ 1.0600269e-02, -1.4934481e+00, 9.1430867e-01, ..., -6.1285675e-01, -3.0706315e+00, -9.9033105e-01], ..., [ 1.7771789e+00, -1.3830042e+00, -1.4351614e+00, ..., -2.6786397e+00, 3.7956804e-02, 6.7189908e-01], [-2.1517308e+00, -5.8123243e-01, -7.7163374e-01, ..., 1.6774191e+00, 7.2239363e-01, 1.3373801e+00], [-8.6465418e-01, -1.3932706e+00, -2.2982714e+00, ..., 1.9587449e+00, -6.2718022e-01, -1.1754386e+00]], [[ 5.2605295e+00, 6.8119764e-01, 1.6433215e+00, ..., 1.4899890e+00, 7.7494907e-01, 1.0885936e+00], [ 1.7135508e+00, 1.7890544e+00, 1.5538380e+00, ..., 4.2714515e+00, 3.4532502e+00, 4.0540075e+00], [ 3.2757509e-01, 2.8093519e+00, 4.4473543e+00, ..., 1.6302650e+00, 2.0791094e+00, -2.7314346e+00], ..., [ 3.1872306e+00, 2.1063502e+00, 4.4839258e+00, ..., 8.6034179e-01, 3.7707591e+00, 3.9809742e+00], [-2.0055294e-02, -4.3134212e-02, 2.1313593e+00, ..., 3.0318618e+00, 2.2852294e+00, 3.9968524e+00], [ 2.1781492e+00, 3.6937137e+00, 1.5003638e+00, ..., 3.5955300e+00, 1.7056749e+00, 1.9585730e+00]], [[ 1.1795213e+00, -7.1754062e-01, -1.3523299e-01, ..., -5.6350648e-01, -1.3417213e+00, -5.0127864e-02], [-5.1167816e-01, -7.7823803e-02, -3.1461412e-01, ..., 7.8631788e-01, 5.9256524e-01, 6.9275266e-01], [-1.3142396e+00, 7.9331988e-01, 5.0062788e-01, ..., -6.4525604e-03, -3.3234254e-02, -2.1546085e+00], ..., [-2.0651843e-01, 1.1771068e-02, 1.3835690e+00, ..., 2.8883666e-03, 4.5511311e-01, 2.9804629e-01], [-9.0822458e-01, -1.3634090e+00, -4.2348909e-01, ..., 2.9903316e-01, -5.9180021e-01, 5.1938176e-01], [-3.7974668e-01, 6.5785772e-01, -4.8025602e-01, ..., 1.5578230e-01, -8.5666311e-01, -8.2990326e-02]]], [[[ 2.7270940e-01, 1.6803369e-01, 6.4784336e-01, ..., -8.6817765e-01, 2.4317000e+00, 9.9560642e-01], [-1.0902294e+00, -1.5418210e+00, -6.4213789e-01, ..., 3.8346985e-01, -2.2009264e-01, -1.4083362e+00], [-1.2999996e+00, -1.0029310e+00, -8.0927563e-01, ..., -9.6844232e-01, 4.7647089e-02, -1.7528368e+00], ..., [ 7.9181468e-01, -7.1245348e-01, -1.2355906e+00, ..., -4.4910422e-01, 7.0296872e-01, -1.8157486e+00], [ 8.5229218e-01, -3.9036795e-01, 3.7029549e-01, ..., -2.0579123e+00, 9.2259049e-03, -1.2485095e+00], [-1.0421257e+00, 9.6360290e-01, -1.9165359e+00, ..., -1.5525728e+00, -2.7757692e+00, 5.9844279e-01]], [[ 2.0120070e+00, 2.5763493e+00, 2.5311258e+00, ..., 2.0375581e+00, 1.6430848e+00, 4.5296006e+00], [-6.4119029e-01, 3.2270002e-01, 2.7286339e+00, ..., 3.4792902e+00, 4.8433290e+00, 1.8760866e+00], [ 5.2160606e+00, 5.8354855e-01, 1.9910555e+00, ..., 3.8761294e-01, 3.4568546e+00, 2.2840927e+00], ..., [ 2.4697292e+00, 3.1099756e+00, 4.5984769e+00, ..., 3.1638999e+00, 1.7895203e+00, 5.1426482e-01], [ 2.0174649e+00, 3.7343421e+00, 1.3838698e+00, ..., 6.8948352e-01, 1.9830887e+00, -1.2911747e+00], [ 2.2970469e+00, 2.8243198e+00, 8.7906146e-01, ..., 3.2837601e+00, 1.0420291e+00, 4.1244802e+00]], [[-4.5218289e-01, -1.1248827e-02, -3.9010030e-01, ..., -2.1441557e-01, -8.8925439e-01, 1.0432711e+00], [-1.5277631e+00, -6.0763943e-01, 8.2450414e-01, ..., 5.1565582e-01, 9.1227055e-01, -4.1257131e-01], [ 1.1678007e+00, -8.4806198e-01, -4.1370481e-01, ..., -9.4888353e-01, 2.4556525e-01, 2.7058780e-02], ..., [-2.6444227e-01, 6.4803612e-01, 1.4935874e+00, ..., 1.9097075e-02, -6.0670388e-01, -3.2186458e-01], [-5.2368152e-01, 6.9923353e-01, -6.0641676e-01, ..., -3.2536793e-01, -3.0933461e-01, -1.7596698e+00], [-5.7884902e-01, -9.0141267e-02, -4.4471401e-01, ..., 3.5021925e-01, -1.7998603e-01, 6.4285696e-01]]]], dtype=float32)] 这里可以把input的第一个维度,也就是batch_size修改成任意数值,程序都可以运行。此时我们打印下model的信息。 import onnx import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load("./model.onnx") inputs = model.graph.input outputs = model.graph.output for i in inputs: i.type.tensor_type.shape.dim[0].dim_param = 'batchsize' for o in outputs: o.type.tensor_type.shape.dim[0].dim_param = 'batchsize' print(model) # onnx.checker.check_model(model) # onnx.save_model(model, 'dynamic_model.onnx') # sess = onnxruntime.InferenceSession('./dynamic_model.onnx') # input = np.random.randn(3, 3, 244, 244).astype(np.float32) # print(sess.run(['output'], {'input': input})) 运行结果 ir_version: 8 graph { node { input: "input" input: "weight" input: "bias" output: "output" op_type: "Conv" attribute { name: "group" i: 1 type: INT } attribute { name: "kernel_shape" ints: 1 ints: 1 type: INTS } attribute { name: "pads" ints: 0 ints: 0 ints: 0 ints: 0 type: INTS } attribute { name: "strides" ints: 1 ints: 1 type: INTS } } name: "graph" initializer { dims: 3 dims: 3 dims: 1 dims: 1 data_type: 1 float_data: 0.45837152004241943 float_data: 0.10209446400403976 float_data: 1.0382566452026367 float_data: -0.09292714297771454 float_data: 1.58871591091156 float_data: 0.3746287226676941 float_data: -0.35588690638542175 float_data: 0.7165427207946777 float_data: 0.10244251787662506 name: "weight" } initializer { dims: 3 data_type: 1 float_data: -0.36782845854759216 float_data: 2.305680513381958 float_data: -0.13051341474056244 name: "bias" } input { name: "input" type { tensor_type { elem_type: 1 shape { dim { dim_param: "batchsize" } dim { dim_value: 3 } dim { dim_value: 244 } dim { dim_value: 244 } } } } } output { name: "output" type { tensor_type { elem_type: 1 shape { dim { dim_param: "batchsize" } dim { dim_value: 3 } dim { dim_value: 244 } dim { dim_value: 244 } } } } } } opset_import { version: 17 } 这里我们可以看到在input中的第一个dim中变成了dim_param: "batchsize" 节点的增加和删除 增加节点 import onnx import onnx.helper as helper import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load('./model.onnx') nodes = model.graph.node new_node = helper.make_node(op_type='Relu', name='relu1', inputs=['conv1'], outputs=['output']) nodes.append(new_node) nodes[0].output[0] = 'conv1' onnx.checker.check_model(model) onnx.save_model(model, 'add_model.onnx') input = np.random.randn(1, 3, 244, 244).astype(np.float32) sess = onnxruntime.InferenceSession('./add_model.onnx') print(sess.run(['output'], {'input': input})) 运行结果 [array([[[[1.5453527 , 0. , 0. , ..., 0.04255658, 0. , 0.40214583], [0. , 0.5019511 , 0. , ..., 0.34235588, 0.36859825, 0. ], [0. , 0. , 0.34334645, ..., 0. , 0. , 0. ], ..., [1.1857387 , 1.0710502 , 0. , ..., 0. , 1.8497316 , 0. ], [0.37889728, 0. , 0. , ..., 0. , 0. , 0. ], [0.73697627, 0. , 0.4978644 , ..., 0. , 0. , 0.32394186]], [[1.2723072 , 0. , 0.66669345, ..., 5.6399436 , 1.4827138 , 2.7300682 ], [4.5705633 , 2.9856906 , 2.9005556 , ..., 3.505543 , 4.7502317 , 0. ], [1.5251542 , 3.3182473 , 3.8036246 , ..., 0. , 1.6024959 , 1.4051957 ], ..., [1.7204559 , 4.551407 , 4.172427 , ..., 0.9121852 , 3.3593512 , 4.6163626 ], [0.2845726 , 0.13289118, 3.3601975 , ..., 3.9331636 , 0.3700601 , 1.5711328 ], [3.3283763 , 2.128338 , 2.1621299 , ..., 1.7635765 , 0. , 2.1479769 ]], [[0. , 0. , 0. , ..., 1.4292918 , 0. , 0.46683455], [1.0534286 , 0. , 0.02258705, ..., 0.4342987 , 1.1339298 , 0. ], [0. , 0.50237906, 0.20627443, ..., 0. , 0. , 0. ], ..., [0. , 0.78040606, 1.003104 , ..., 0. , 0. , 1.0389903 ], [0. , 0. , 0. , ..., 0.74816215, 0. , 0.02678718], [0.26068228, 0. , 0. , ..., 0. , 0. , 0. ]]]], dtype=float32)] 这里我们再来打印下model的信息 import onnx import onnx.helper as helper import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load('./model.onnx') nodes = model.graph.node new_node = helper.make_node(op_type='Relu', name='relu1', inputs=['conv1'], outputs=['output']) nodes.append(new_node) nodes[0].output[0] = 'conv1' print(model) # onnx.checker.check_model(model) # onnx.save_model(model, 'add_model.onnx') # # input = np.random.randn(1, 3, 244, 244).astype(np.float32) # sess = onnxruntime.InferenceSession('./add_model.onnx') # print(sess.run(['output'], {'input': input})) 运行结果 ir_version: 8 graph { node { input: "input" input: "weight" input: "bias" output: "conv1" op_type: "Conv" attribute { name: "group" i: 1 type: INT } attribute { name: "kernel_shape" ints: 1 ints: 1 type: INTS } attribute { name: "pads" ints: 0 ints: 0 ints: 0 ints: 0 type: INTS } attribute { name: "strides" ints: 1 ints: 1 type: INTS } } node { input: "conv1" output: "output" name: "relu1" op_type: "Relu" } name: "graph" initializer { dims: 3 dims: 3 dims: 1 dims: 1 data_type: 1 float_data: 0.45837152004241943 float_data: 0.10209446400403976 float_data: 1.0382566452026367 float_data: -0.09292714297771454 float_data: 1.58871591091156 float_data: 0.3746287226676941 float_data: -0.35588690638542175 float_data: 0.7165427207946777 float_data: 0.10244251787662506 name: "weight" } initializer { dims: 3 data_type: 1 float_data: -0.36782845854759216 float_data: 2.305680513381958 float_data: -0.13051341474056244 name: "bias" } input { name: "input" type { tensor_type { elem_type: 1 shape { dim { dim_value: 1 } dim { dim_value: 3 } dim { dim_value: 244 } dim { dim_value: 244 } } } } } output { name: "output" type { tensor_type { elem_type: 1 shape { dim { dim_value: 1 } dim { dim_value: 3 } dim { dim_value: 244 } dim { dim_value: 244 } } } } } } opset_import { version: 17 } 这里我们可以看到增加了一个relu1的节点,并且第一个节点的output是conv1,第二个节点的input是conv1,output是output。 删除节点 import onnx import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load('./add_model.onnx') nodes = model.graph.node for node in nodes: if node.name == 'relu1': nodes.remove(node) nodes[0].output[0] = 'output' onnx.checker.check_model(model) onnx.save_model(model, 'del_model.onnx') input = np.random.randn(1, 3, 244, 244).astype(np.float32) sess = onnxruntime.InferenceSession('./del_model.onnx') print(sess.run(['output'], {'input': input})) 运行结果 [array([[[[-8.5923064e-01, -4.2249173e-01, 3.8687822e-01, ..., -4.8348337e-02, 3.1652334e-01, -5.7166600e-01], [ 3.1469372e-01, -9.4796360e-01, -2.4245100e+00, ..., 4.1007617e-01, -1.4098099e+00, 6.7472184e-01], [-1.2910874e+00, 1.6070822e-01, -1.0217074e+00, ..., 7.1467435e-01, 1.5835044e-01, -6.4228356e-01], ..., [-2.5442154e+00, -8.8969648e-01, 1.1389736e+00, ..., 1.7202379e+00, -1.1968368e+00, -3.3861694e-01], [-9.0216339e-01, 4.8469666e-01, -9.5050204e-01, ..., 4.0511075e-01, -1.0113320e-01, 1.8743831e+00], [ 3.2901958e-01, 4.3780953e-02, 1.4250931e+00, ..., -1.4544667e+00, 9.0659869e-01, 1.7170597e+00]], [[ 1.3439684e+00, 3.0856354e+00, 2.7811766e+00, ..., 4.1714394e-01, -3.3547878e-02, 1.1771207e+00], [ 2.1574910e+00, 2.1122241e+00, -5.8333945e-01, ..., 1.9629711e+00, 3.4840956e+00, 6.1747317e+00], [ 5.2136226e+00, 4.8688288e+00, 1.4613919e+00, ..., 4.1095753e+00, 1.4553337e+00, 3.5171165e+00], ..., [ 7.3736429e-02, 8.4109855e-01, 5.7113109e+00, ..., 3.6336284e+00, 4.4551125e+00, 3.4602299e+00], [ 1.1054695e+00, 2.7417006e+00, 4.9065466e+00, ..., 2.1775680e+00, 4.4132576e+00, 2.3781679e+00], [-1.2788355e+00, 2.5300267e+00, 3.2560487e+00, ..., 2.2025514e+00, 4.2551570e+00, 3.5148311e+00]], [[-8.5124874e-01, 3.1858414e-01, 3.3686757e-03, ..., -1.1497847e+00, -1.1996644e+00, -9.6176589e-01], [-4.2057925e-01, -1.8098265e-01, -7.4302059e-01, ..., -3.5920531e-01, 7.0454830e-01, 1.8304255e+00], [ 1.4177717e+00, 8.4456313e-01, -1.6396353e-01, ..., 4.2133337e-01, -4.6482396e-01, 6.6906375e-01], ..., [-1.0060047e+00, -1.2088763e+00, 1.2608007e+00, ..., 5.1739502e-01, 8.9526463e-01, 7.2866821e-01], [-3.5698372e-01, -5.9943002e-01, 1.0040566e+00, ..., 3.1322885e-01, 3.4513384e-01, -6.2404698e-01], [-2.0622578e+00, 3.9633280e-01, 2.1701033e-01, ..., 2.6992482e-01, 4.4787437e-01, 2.1187775e-01]]]], dtype=float32)] 替换节点 现在我们将add_model.onnx中的Conv节点替换成Squeeze节点(压缩维度) import onnx import onnx.helper as helper import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load('./add_model.onnx') new_node = helper.make_node(op_type='Squeeze', inputs=['input'], outputs=['conv1'], name='squeeze1') nodes = model.graph.node nodes.append(new_node) for node in nodes: if node.op_type == 'Conv': nodes.remove(node) # onnx.checker.check_model(model) onnx.save_model(model, 'replace.onnx') input = np.random.randn(1, 3, 244, 244).astype(np.float32) sess = onnxruntime.InferenceSession('./replace.onnx') print(sess.run(['output'], {'input': input})) 运行结果 [array([[[0. , 1.1258854 , 0. , ..., 0.54984987, 0.19069785, 0. ], [1.1481465 , 0. , 1.9025986 , ..., 0. , 0.11273875, 0. ], [1.57912 , 0. , 0. , ..., 0. , 1.7471381 , 0. ], ..., [0.42386332, 0.30908984, 0. , ..., 0. , 0. , 1.8173866 ], [0.07642962, 0.31224537, 0. , ..., 1.6805407 , 2.0282576 , 0. ], [0. , 0.2521538 , 0. , ..., 0. , 0.6431213 , 0.5844705 ]], [[0. , 0. , 0. , ..., 0.23725364, 0.22994171, 0.316093 ], [0.85044146, 1.2757416 , 0.28854838, ..., 0. , 0. , 0. ], [0. , 0. , 1.1362596 , ..., 1.8543358 , 1.1296074 , 0.5114057 ], ..., [0. , 0.00810617, 0. , ..., 1.0819261 , 1.707781 , 0. ], [0. , 0.6385371 , 0. , ..., 0.6565783 , 1.457183 , 0. ], [0. , 0.8315589 , 1.4111192 , ..., 1.0682058 , 0.17328343, 2.3547616 ]], [[0.2426068 , 0. , 0. , ..., 0.89054537, 0.98760164, 0. ], [1.1344411 , 0.8732987 , 0. , ..., 0. , 0. , 0. ], [0. , 0.3664789 , 1.4099371 , ..., 0. , 0.0588427 , 0.5932818 ], ..., [0. , 0.68438137, 0.8869638 , ..., 0. , 0. , 1.4681839 ], [0. , 0. , 0. , ..., 0.16630006, 1.9389246 , 0. ], [0. , 0. , 0.03726726, ..., 0.86296386, 0. , 0. ]]], dtype=float32)] 这里需要注意的是,如果我们将# onnx.checker.check_model(model)的注释打开,运行是会报错的,因为我们添加的新节点squeeze1是在relu1之后的,虽然无法通过检查,但是是可以使用运行时来运行的。那如何才能即能运行又让检查也可以通过。 import onnx import onnx.helper as helper import onnxruntime import numpy as np if __name__ == '__main__': model = onnx.load('./add_model.onnx') new_node = helper.make_node(op_type='Squeeze', inputs=['input'], outputs=['conv1'], name='squeeze1') nodes = model.graph.node # nodes.append(new_node) for idx, node in enumerate(nodes): if node.op_type == 'Conv': nodes.remove(node) nodes.insert(idx, new_node) onnx.checker.check_model(model) onnx.save_model(model, 'replace.onnx') input = np.random.randn(1, 3, 244, 244).astype(np.float32) sess = onnxruntime.InferenceSession('./replace.onnx') print(sess.run(['output'], {'input': input})) 这里主要就是调换一下新节点的位置就好了。 ONNXRuntime介绍 ONNXRuntime是微软推出的一个推理框架,可以非常方便的运行ONNX模型,官方GitHub:https://github.com/microsoft/onnxruntime

优秀的个人博客,低调大师

hbase 整理

时间同步设置 ntpdate.cn.pool.ntp.orgzookeeper分布式系统协调工具 高可用协调工具zookeeper 上的数据存在于每个server节点上 每个节点上的zookeeper节点上保存的数据都是全量数据zookeeper上保存的数据的方式和文件系统保存数据一样 hbase的数据模型sql 在sql的数据库中包含两种元素即 模式和元组nosql:在nosql里面一般更强调元组而弱化模式 有些nosql中没有模式 有些nosql有比较弱的模式 比方说有些表而没有明确字段 或者有表有更为冠以上的字段 hbase 是nosql中的一种 它的设计和他使用都是反模式的。它里面不是完全没有模式 ,它的模式分为三层。1.namespace 类似于mysql 的database2.table 类似于MySQL的table3.column 雷速与mysql的table把他认为是mysql里面一个表的多个字段按照找某个条件分成多组一个hbase 中可以创建若干个2namespace namespace一般用来做部门或者应用之间的数据隔离一个namespace下可以创建若干个table table做不通种类的数据之间的隔离一个列簇下面可以有若干个列簇 hbase 的劣质可以使任意类型hbase对其中所有存储的数据不做任何类型上的限制hbase在存储数据的位置不作任何类型上的限制hbase在存储书籍肚饿位置处统一的吧数据转换成字节数组来进行保存的。 namespace的基本操作create_namespace 'bd2o'list_namespace 展示hbase有多少个namespacelist_namespace_table 'bd20' 展示bd20数据下有多少张表 drop_namespace 'bd20' table 的基本操作创建表create 'bd20:table_name' ,'column_family'describe 'bd20:table_name'disable 'bd20:table_name'drop 'bd20:table_name' --alter tablealter 'bd20:table1',NAME=>'c1',method=>'delete'alter table 'bd20:tale1',NAME=>'c2'create create 'cms_link','primary_info','system_info' mysql 上所说的一条数据值的是一行数据hbase中所说的一条数据值的是一行数据中的单元格put ‘cms_link’,'1','primary_info:category_id','19' hbase 底层数据存储hbase 的底层数据是按照挑来保存的,每一条数据在底层数据文件里面占据一个存储空间。每条数据的格式如下rowkeytimestampfamily_column hbase的数据文件是吧每条数据以kv的形式来存储的因为key上的大部分数据都是冗余的,范式我们自己设计的kety上的数据 尽可能的限定他们的长度rowkey在设计师要舍得尽可能的端列簇在设计上要尽可能的端,要尽可能的表达劣质的数据内容的意义,比方说姓名,年龄等。hbase架构原理hbase中的没一张表都是分布式存储的hbase 吧一张表的tale划分为多个region 每个rigion可以散布在不同的regionserver 上来对外提供服务,hbase 是根据兴业划分region 也就是说hbase是根据rowkey 来划分region的。每个region都有自己的rowkey上下线,第一个region没下线,最后一个region 没有上下限,当一条记录需要保存在habse 中,hmaster 会判断这条记录的rowkey应该分布在哪个region上,然后转交给这个region所负责的regionserver来执行读写操作。 hbase 的region 的大小会随着里面的数量的增加而增加。当以region的体谅超过256M时,这个region就会分裂。分裂的过程有hmaster主导,由hregionserver执行,在分裂的过程中,不对外提供读写服务。hbase 中如果在定义表示不对表进行预分区。那么整个hbase 表就只有一个region 为了尽可能的皮面分裂最好。create ‘stable1’,‘i’,SPLITS=>['100','200','300','400','500']hbase 根据rowkey查找一个表tableA数据的过程 根据root表来确定meta的位置,root表存在于zookeeper 中 根据元数据定位taleA的位置,元数据保存在hbase的meta 表中。定位tableA的位置,然后根据rowkey来确定要查找记录的准确region 根据region定位其所在。regionserver regionserver根据rowkey就检索数据文件中记录的位置从而获取数据。 hbase中的数据来源1.来源自自己有的数据文件mapreducehivespark bulk loading2.来自业务系统不断增加的数据记录。java apishell 其他语言 +thrift服务hbase api操作1.构建配置参数对象,使用hbaseConfiguration来创建。2.创建Connection 对象。使用ConnectionFactory 来创建。3.如果执行ddl来操作的话需要Admin对象。通过Connection来获取。4.如果执行dml操作的话需要Table对象,通过Connection来获取。往hbase里面插入数据 需要Put对象,一个Put对象代表一行数据。mapreduce读写hbaseTableInoutFormatTableOutputFotmatTableMapperTableReducerTableMapReduceUtil写hbase 需要在job上配置输出格式为TableOutputFormat,reduce 的输出kv必须满足写入hbase表的kv类型可以通过写reduce继承TabeReducer来解决这个kv类型的限定。在Job中还得设置目标表,可以通过调用。

优秀的个人博客,低调大师

CUDA编程整理

CUDA编程的理论部分可以参考模型部署篇 中的GPU 的 CUDA 编程方法。 虽然CUDA有很多的C代码,这里我们主要以C++为主。一个完整的CUDA程序,需要经历7个步骤 设置显卡设备 分配显存空间 从内存到显存拷贝数据 执行CUDA并行函数 CUDA函数结束后,将结果从显存拷贝回内存 释放显存空间 设备重置 如果是单GPU的话可以省略1跟7两个步骤。 #include <stdio.h> #include <stdlib.h> #include <cuda_runtime_api.h> #include <iostream> /* 核函数 */ __global__ void kernelFunc(float *a) { a[threadIdx.x] = 1; } int main(int argc, char **argv) { //设置显卡设备 cudaSetDevice(0); //分配显存空间 float *aGpu; cudaMalloc((void**)&aGpu, 16 * sizeof(float)); //从内存到显存拷贝数据 float a[16] = {0}; cudaMemcpy(aGpu, a, 16 * sizeof(float), cudaMemcpyHostToDevice); //执行CUDA并行函数 kernelFunc <<<1, 16>> >(aGpu); //CUDA函数结束后,将结果从显存拷贝回内存 cudaMemcpy(a, aGpu, 16 * sizeof(float), cudaMemcpyDeviceToHost); for (int i = 0; i < 16; i++) { printf("%f", a[i]); } return 0; } CUDA的源码文件以.cu为后缀,编译命令如下(需要先安装CUDA,安装方式可以参考乌班图安装Pytorch、Tensorflow Cuda环境 ) nvcc main.cu -o main 运行结果 1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000

优秀的个人博客,低调大师

LocalDateTime API 整理

对象介绍 Object desc format LocalDateTime 日期时间 yyyy-MM-dd'T'HH:mm:ss LocalDate 日期 yyyy-MM-dd LocalTime 时间 HH:mm:ss 获取对象实例 Function Desc now 获取当前时间 of 获取指定时间 判断 Prefix is Suffix desc after 是否在传入对象, 之后 before 是否在传入对象, 之前 equal 相等 supported 检查是否受支持( 字段支持, 单位支持 ) Demo LocalDateTime maxTime = LocalDateTime.now().plusSeconds(SendMessageConfig.getDelayTime()); if (messageDTO.getSendTime().isAfter(maxTime)) { return ViewUtils.build(ViewCodeEnum.ERROR.getCode(), "验证码超时", null); } 运算 Operation Prefix plus 加 Prefix minus 减 prefix get 获取 时间单位 Suffix Desc nanos 纳 seconds 秒 hours 小时 minutes 分钟 days 天 weeks 周 months 月 years 年 获取时间戳 Instant.now().toEpochMilli() System.currentTimeMillis()

优秀的个人博客,低调大师

Java知识整理

Java面向对象的特征有哪些方面? 抽象 忽略与主题无关的其他信息 继承 extends的意思是扩展,即子类是父类的扩展。扩展的意义在于不需要重新造轮子,这也是继承的意义。 子类继承父类就可以得到父类的全部属性和方法,除了私有的属性和方法以及父类的构造方法。 Java 只允许单继承,目的是不想使得继承链过于复杂而导致系统难以维护。如果想”多继承”,可以使用接口代为实现。 如果没有指定父类,那么它的直接父类是java.lang.Object 继承会导致重写,子类有时会需要重写父类的方法,自身的行为替换父类的行为。方法的重写是实现多态的必要条件。重写需要符合三个条件 方法名、形参列表相同。返回值类型和异常类型,子类需要小于父类。访问权限,子类需要大于父类。 封装 封装的意思是想让你看到的就让你看到,不想让你看见的就隐藏起来。优点是高内聚低耦合。 高内聚:封装细节,保证安全。 低耦合:简化使用,便于扩展。 Java使用访问修饰符实现封装 private:私有的,只能在同一个类中使用 default:默认的,可以在同一个包中使用 protected:受保护的,可以由子类使用 public:公开的,可以被所有的类访问 一般使用peivate修饰属性,但提供get/set方法供外界访问。 多态 同一个方法调用,不同的对象可能会有不同的行为。例如同样是十一假期,有人会出去旅游,有人会宅在家里。 多态是方法的多态不是属性的多态。 多态的必要条件,继承、方法重写、父类引用指向子类对象。 java.util.comparator 接口是干什么? 如果我们需要控制某个类的次序并且这个类本身不支持排序,那么就可以建立一个类比较器来进行排序。 类比较器的只需要实现 java.util.Comparator 接口并实现对应的方法就可以了~ String类为什么使用final修饰 ? 如果String类可以被修改的话,那么就很难做到修改A中的String值的同时而不修改B中的String值。原因是String的两个引用有可能会来自同一块内存地址。 final修饰符是线程安全的,在创建后就不能修改,因此不会产生线程安全问题,所以可以在多线程环境下使用。 在现实生活中,如密码,如手机号码,如文件名称之类都是以String类型的方式存储,如果String不使用final修饰,那么就有可能账号和密码会被人不知不觉地修改了。Interger等包装类型也是同样的道理。而用于精确计算的BigDecimel类则不同,它主要是为了解决二进制不能很好的表示浮点数而设计的。 String类还是用了Static修饰符修饰,目的是方便调用。如果不使用Static,那么就不能在静态方法下直接调用String的方法或值,而必须使用new进行实例化。 String常量池的存储位置 在JDK1.6中 String存储在jvm的PermGen中,并且限制大小。同时该区域还用于存储类信息。 在JDK1.7中 String存储在heap 中, 大小无限制。 序列化是什么和什么时候使用序列化? 序列化在Java中一般指对象的序列化。具体含义是将对象转换为字节序列即二进制的过程。在这个过程中不仅仅保留当前对象的数据,而且会以递归地形式保存引用的每个对象的数据。利用对象序列化可以实现对对象的”深复制”。即复制对象本身以及引用的对象本身。序列化一个对象就可以得到整个对象序列。而反序列化则是将字节序列恢复为对象的过程。 pojo implements Serializable 序列化通常有两种用途,都是将对象写入字节流中。 当需要把对象的字节序列永久的保存到硬盘上时,一般以文件的形式。字节序列需要在网络上传输时。P:选择一种高效的序列化和反序列化方式可以提升整个架构的性能。 Java虚拟机内存中的堆区(heap),栈区(stack),和静态区(static/method)。 堆区 存储真正的对象,每个对象都包含一个与之对应的class信息,这个class信息的作用是得到操作指令。 特点:只有一个堆区,随着JVM的启动而创建,所有线程共享区域,因此不存放基本类型和对象引用,只存放对象本身。如果堆内存剩余的空间不足以创建对象,JVM会抛出OutOfMemoryError的错误。P:堆一般由程序员分配。 栈区 存放基本数据类型和引用数据类型。P:Strtus中的压栈出栈操作 特点:每个线程包含一个栈区,并且栈中的数据都是私有的。栈中的数据由编译器自动分配释放。当一段代码定义一个变量时,JVM就为该变量在栈中开辟空间,当该变量退出作用域时,JVM就释放该空间,这个空间就随时可以被其他变量使用。P:栈分为三个部分。基本类型变量区、执行环境上下文、操作指令区。 静态区 又名方法区,是所有线程共享的区域,存放所有的类信息(类名,类方法信息,类字段信息)和static修饰的变量。 特点:整个程序中永远唯一的元素。全局变量和静态变量都放一起。不同的是,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域。 HashMap和HashTable的区别? HashMap是HashTable的轻量级的非线程安全的实现。它们都完成了Map接口。采用的算法hash/rehash都大概一样,底层也都是采用数据加链表结构实现,所以性能上不会有很大差异。除了HashMap和HashTable,Hash集合中还有一个HashSet,它里面存储的并不是key-value结构,仅仅是存储不重复的元素,相当于简化版的HashMap,内部采用HashMap实现,所有的value都是同一个Object,只是包含HashMap中的key而已。 主要的区别在于HashMap允许空键值,而HashTable不允许。 由于HashMap是非线程安全,所以效率上会高于HashTable。 HashMap将HashTable中的contains()改成了containsValue()和containsKey()。原因大概是便于阅读吧~ HashMap直接继承Map,而Hashtable继承Dictionary。 HashMap在多线程的环境下需要为方法提供外同步(Collections.synchronizedMap),而HashTable不需要。 int类型和Integer类型有什么区别? Java提供了两种不同的类型,数据类型和引用类型,int是Java的数据类型,Integer是Java的引用类型。不仅是int,Java为每个基本类型都准备了对应的引用类型。它们的行为和语义都不同。 大小和速度的问题 存储数据结构问题 缺省值不同的问题 String、StringBuffer和StringBuilder的区别? String是作为常量在Java中被定义的,而StringBuffer是变量,意味着它可以修改对象内容。如果需要经常对一个字符串进行修改那么就可以考虑使用StringBuffer,因为它不会生成新的对象。 JDK1.5新增了一个可变的字符序列StringBuilder,它与StringBuilder有兼容但不同步的API,是StringBuilder的一个简单替换,用于字符串缓冲区被单个线程使用的时候,Java建议优先使用StringBuilder,因为在大多数实现中,它比StringBuffer速度要快,并且使用方法基本相同。 什么是运行时异常和非运行时异常? 非运行时异常指的是编译时异常或者说受检异常,Java编译器强制要求这类异常在代码编写的时候就处理,或抛出或捕获。运行时异常指的是虚拟机正常运行中可能会遇到的异常。 说说你常见的异常 *Java.lang.NullPointerException 程序遇上了空指针,简单地说就是调用了未经初始化的对象或者不存在的对象,这个错误经常出现在创建图片(路径),使用数组(初始化)中。 java.lang.ClassNotFoundException 类不存在,注意检查类的名称和路径是否正确 java.lang.ArrayIndexOutOfBoundsException 数组越界访问 java.lang.IllegalArgumentException 方法参数错误 java.lang.IllegalStateException org.springframework.web.util.NestedServletException 写一个Singleton单例模式吧 单例模式的特点 单例模式中的类只能有一个实例单例类必须自己创建自己的实例单例类必须给其他对象提供实例 在计算机系统中,线程池、端口、缓存、日志、对话框、打印机、显卡的驱动程序常常被设计成单例模式,这主要是为了避免发生诸如两台计算机同时打印时打印机该如何工作的问题,还有就是端口冲突的问题。再例如听歌,一般音乐软件同一时间只能播放一首歌。,虽然你有两个耳朵,但是这不代表你开心同时听两首歌~ 单例模式有两种表现形式 // 第一种形式 Class Singleton { Private Singleton(){} Private static Singleton instance = new Singleton(); // 提供一个供外界访问本类的静态API Public static Singleton getInstance(){} } // 第二种形式 Class Singleton { Private static Singleton instance = null; // 创建静态访问器,为了防止两个线程同时进行对象的创建,加上同步锁 Public static synchronized Singleton getInstance(){ If (instance == null) { Instance = new Singleton(); } Return instance; } } J2EE常用的设计模式,并重点说一下工厂模式。 Factory(工厂模式)Factory Method(工厂方法模式)Prototype(原始模型模式)Singleton(单例模式)Adapter(适配器模式)Decorator(装饰模式)Proxy(代理模式)Observer(观察者模式) 工厂模式是最常用的模式之一,根据工厂模式实现的类可以根据提供的数据生成一组类中的某一个类的实例,通过这一组类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作。首先需要定义一个基类,该类的子类通过不同的方法实现了基类中的方法,然后需要定义一个工厂类,工厂类可以根据条件生成不同的子类实例。当得到子类实例侯,开发人员可以调用基类的方法而不必考虑到底返回哪一个子类的实例。 写一个工厂模式 工厂模式是Java中最常用的模式,原理是利用Java反射机制和多态的特性。目的是让程序更灵活,可以使项目并行开发,其中的粘合剂就是接口和配置文件。 Interface InterfaceTest{ Public void getName(); } // 有了接口就可以根据接口 进行并行开发 // 程序员A Class Test1 implements InterfaceTest{ Public void getName(){ System.out.println(“test1”); } } // 程序员B Class Test2 implements InterfaceTest{ Public void getName(){ System.out.println(“test2”); } } // 工厂类 生产接口对象 // 在调用时得到的是接口对象,一个接口变量指向实现该接口的类对象 // 通过键获取值,而不是类的全路径 Class Factory{ Private static Properties pro = new Properties(); Static { try { // 加载配置文件 Pro.load(new FileInputStream(“xxx.xxx”)); } catch (Exception e) { e.printStackTrace(); } } private static Factory factory = new Factory(); private Factory(){}; Public static Factory getFactory() { return factory; } Public InterfaceTest getInterface() { InterfaceTest interfaceTest = null; try { // 根据key,获取value value为类的全路径 String classInfo = pro.getProperty(“name”); // 利用反射生成class对象 Class c = Class.forName(classInfo); Object obj = c.newInstance(); interfaceTest = (interfaceTest )obj; }catch (Exception e) { e.printStackTrace(); } return interfaceTest ; } } // 调用方法 class FactoryTest { public static void main(String[] args) { Factory factory = Factory.getFactory(); // 通过创建的实例调用获得接口对象的方法获取接口对象 InterfaceTest inter = factory.getInterface(); // 调用接口定义的方法 Inter.getName(); } } 说出ArrayList、Vector、LinkedList的存储性能和特性。 ArrayList和Vector都使用数组的方式存储数据。当数组元素大于实际存储的数据时以便增加和插入元素,它们都允许直接按序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据块而插入数据慢,Vector由于使用了synchronized方法保证线程安全,因此性能上较ArrayList差。 LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,索引速度慢。因为插入数据时只需要记录本项的前后项即可,插入速度较快。 Collection和Collections Collection是集合类的上级接口,继承于它的接口主要是Set和List。 Collections是针对集合类的一个帮助类,提供了一系列静态API,实现了对集合的搜索、排序、线程安全化等操作。 final、finally、finalized之间的区别? final用于声明属性、方法和类,分别表示该属性不可变、该方法不可覆盖、该类不可继承。 定义变量时加上final,代表一旦被初始化后就不可改变 将方法声明为final,则说明你已经知道这个方法提供的功能已经满足你要求不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法。只能通过继承这个方法来直接使用。 当final用于类的时候,代表此类在继承树中是一个叶子类,如果你认为此类涉及已经很完美而不需要进行修改和扩展的时候就大胆使用它吧。 finally是异常处理结构语句的一部分,表示该代码块中的代码总是执行。如果是断电或者强行中断程序,则该块中的代码才不会被执行。 finalize是Object类中的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾回收时其他资源的回收,例如关闭文件。 说出你知道的线程同步方法 wait()使一个线程处于等待状态,并释放所持有对象的lock. sleep()使一个正在运行的线程处于睡眠状态,是静态方法,调用此方法要捕捉InterruptedException异常。 notify()唤醒一个处于等待状态的线程。注意是在调用此方法时,并不能确切知道唤醒的是那个等待状态的线程,也不是按优先级,而是由JVM确定唤醒的线程。 allnotity()唤醒所有处于等待状态的线程,注意不是给所有唤醒线程一个对象的锁,而是让它们竞争。 OverLoad 和 Override的区别 方法的重载和重写是Java多态的表现。前者是父子类之间,后者是同一个类之中。如果说子类中定义某方法与父类中的方法名称和参数都相同的话,我们就说该方法被重写了。如果在同一个类中定义了多个同名的方法,它们有不同的参数个数和参数类型,则称之为方法的重载。 interface和abstract 两者是Java面向对象的重要实现。声明方法的存在而不去实现它的类叫做抽象类,它的子类必须全部实现它父类的所有方法,否则它也是一个抽象类。抽象类不能有抽象构造函数和抽象静态方法。也不能被实例化,并且它只能被public、protected修饰符修饰。 接口是抽象类的变体。在接口中所有方法都是抽象的,并且它只可以定义static final的成员变量。Instanceof运算符可以用来决定某对象的类是否实现了接口。 什么情况下使用同步或者异步编程 如果数据将在线程间共享,正在写的数据以后或者已经可能被另一个线程读到,那么这些数据就是共享数据,就必须使用同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,例如下载操作,这个时候不希望程序等待方法的返回,你在下载音乐的同时也可以听音乐,这个时候就需要使用异步编程。类似这种场景下异步途径往往更具有效率。 GC是什么?为什么需要GC? 内存处理是程序员最容易出错的地方,忘记或者错误的内存回收会导致堆栈溢出系统崩溃等严重错误。Java并没有提供显示的释放已分配内存的操作方法,而是提供GC功能,用于自动检测对象是否超出作用域从而达到自动回收内存的目的。这个功能被称之为垃圾回收机制。 垃圾回收的优缺点?和回收机制 优点是程序员不再需要考虑内存管理,同时垃圾回收可以有效地防止内存泄露,有效地使用可用的内存。 缺点则是垃圾回收器作为一个单独的低级别线程运行,在不可预知的情况下对内存已经死亡或者长时间没有使用的对象进行清除和回收,程序员不能监测到异常情况,也不能实时调用垃圾回收器对对象进行垃圾回收。程序员可以手动调用System.gc(),但java语言规范并不保证GC一定会运行。 垃圾回收的机制有:分代复制垃圾回收、标记垃圾回收、增量垃圾回收。 启动一个线程使用start()还是run()? start()使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,但并不意味着线程就会立即执行。此时线程处于就绪状态。 run()线程体,包含要执行线程的内容,当线程得到CPU的时间片,就会开始执行相应线程的此方法。 使用start()开辟线程,使用run()执行线程。 为什么要有多线程? 线程的出现是为了更好的利用CPU,提高程序运行效率。 说一说你开发过程中常见到的runtime exception ClassCastException IndexOutOfBoundsException NullPointerException SystemException 线程池的实现原理 使用线程池对线程进行统一分配,调优,监控可以降低资源消耗,提高相应速度,提高线程的可管理性。 J2EE服务器会在启动时建立若干线程池连接,并一直维持不少于此数目的池连接,当客户端程序需要连接时,池连接程序会返回一个未使用的池连接并标记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量由配置参数决定,当使用的池连接调用完成后,池驱动程序将池连接标记为空闲,其他调用就可以使用这个连接。 forward和redirect forward转发是服务器请求资源,服务器直接访问目标地址的URL,将URL的响应内容读取过来,然后将这些内容再发送给浏览器,浏览器因此并不知道服务器发送的内容是从哪里来的,所以它的地址栏还是原来的地址。 redirect重定向是服务端根据业务逻辑,发送一个验证码,告知浏览器去请求某个地址,一般来说,浏览器会用刚才请求的参数重新请求,所以session、request参数都可以获取到。 Set集合中的元素不能重复,那么怎样区分重复与否呢?使用==还是equals()?它们有何区别? Set里的元素不能重复,那么可以使用iterator()方法区分是否重复。 equals()是判断两个Set是否相等。属于深度比较,为的是当两个分离的对象的内容和类型相匹配的话返回ture。而”==”是比较两个对象的引用是否指向同一个内存地址,也就是判断对象是否分离。 hashCode()通常被设计用于提高性能。它和equals()的区别就在于当两个对象相等(equals()),那么它们就一定拥有同样的哈希值,但如果两个对象的哈希值相等,则不一定代表这两个对象就一定相等。 Java中异常机制的简单原理和应用 当Java程序违反了java的语义规则时,虚拟机就会将发生的错误表示为一个异常。违反语义包括了两种情况。 Java类库内置的语义检查。如果数组下标越界则会引发IndexOutBoundsException;访问null对象时会引发NullPointerException。另一种是程序员扩展的语义检查,自定义异常并自由选择在何时使用throw关键字引发异常。所有自定义的异常都必须继承java.lang.Thowable。 XML文档定义有几种形式?它们有何本质区别?解析XML文档有几种方式? 两种形式:dtd和schema dtd(data type definition) 数据类型定义,用于描述XML文档的文档结构,是早期的XML文档定义形式。 schema 本身基于XML编写,在类型和语法的限定能力上比dtd强,处理也方便,因此正逐渐代替dtd成为新的模式定义语言。 本质区别:schema本身是xml,可以被xml解析器解析。(这也是从DTD上发展schema的根本原因) 解析方式:DOM、SAX、STAX等 DOM 处理大型文件时性能下降厉害,这个问题是由DOM的树结构造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档都装进内存,便于对XML的随机访问。 SAX 事件驱动型的XML解析方式。它按顺序读取XML文件,而不需要一次全部装载整个文件。当遇到像文件开头,文档结束或者标签开头与标签结束时会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问。 STAX Streaming API for XML(StAX) Servlet Public void init(ServletConfig config); Public ServletConfig getServletConfig(); Public String getServletInfo(); Public void service(ServletRequest request, ServletResponse response); Public void destroy(); - init()方法在servlet生命周期中仅执行一次,在服务器装载servlet时执行。缺省的init()通常是符合要求的,不过也可以根据需要override,比如管理服务器端资源,一次性装入Gif图像,初始化数据库连接等。由于缺省的init()中设置了servlet的初始化参数,并使用了ServletConfig对象参数来启动配置,所以覆盖init()时,需要调用super.init()以确保仍然执行这些任务。 - service()是servlet的核心方法,在调用service()之前,应确保已完成了init()。对于HttpServlert,每当用户请求一个HttpServlet对象,该对象的service()就会被调用,HttpServlet缺省的service()中的服务功能就是调用与HTTP请求的方法对应的do功能,所以对于HttpServlet,一般都重写doPost()或者doGet()。 - destroy()在servlet的生命周期中也仅执行一次,在服务器停止并卸载servlet时执行,将servlet作为服务器进程的一部分进行关闭。缺省的destroy()是符合要求的,但也可以override。比如在卸载servlet时将统计的数字保存在文件中,比如关闭数据库连接。 - getServletConfig()返回一个servletConfig对象。该对象用来返回初始化参数和servletContext。servletContext接口提供有关servlet的环境信息。 - getServletInfo()提供有关servlet的信息,比如作者、版本、版权。 #throw和throws >throw语句用来明确地抛出一个异常 >throws用来表明一个成员函数可能抛出的各种异常 #排序有哪几种方法?请列举并口述用java实现快速排序。 >排序的方法有 * 插入排序(直接插入排序,希尔排序) * 交换排序(冒泡排序,快速排序) * 选择排序(直接选择排序,堆排序) * 归并排序 * 分配排序(箱排序,基数排序) >快速排序的伪代码 从a[0:n-1]中选择一个元素作为middle,该元素为支点。 将余下的元素分割为left和right两段。使left中元素都小于等于支点,而right中的元素都大于等于支点。 递归地使用快速排序方法对left进行排序,然后是right 所得出的结果为left+middle+right; #MVC的各个部分都有那些技术实现?怎么实现的? - Model 代表着业务逻辑,通过JavaBean实现 - View 是应用的表现层,用于用户交互,使用JSP页面技术实现 - Controller 应用程序处理过程控制,一般是Servlet >优点:开发效率高;程序灵活性和扩展性好;代码重用度高;便于人员分工。 >缺点:代码复杂度增加;代码数量增加;不适合开发小型程序。 #java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?Stop()和suspend()为何不推荐使用? >有两种方法,分别是继承Thread类和实现Runnable接口。 >使用关键字synchronized修饰同步方法 >反对使用stop()是因为不安全。它会接触由线程获取的所有锁定,而且如果对象处于一种不连贯的状态,那么其他线程能在那种状态下检查和修改他们,结果很难检查出真正的问题所在.suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获取的锁定,此时,其他线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。对任何线程来说,如果它们都想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便使用wait()命令其进入等待状态,若标志指出线程应当恢复,则使用一个notify()重新启动线程。 #java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类? >字节流、字符流。字节流继承InputStream OutputStream 字符流继承于InputStreamReader OutputStreamWriter >在java.io包中还有许多其他的流,主要是为了提高性能和使用方便 #内部类可以引用他包含类的成员吗?有什么限制? >一个内部类对象可以访问创建它的外部类对象的内容。 >内部类如果不是static,那么它可以访问创建它的外部类对象的所有属性。如果是static,即为nested class,那么它只可以访问创建它的外部类对象的所有static属性。 >一般类只有public或者package等访问修饰符。而内部类可以实现static、protected、private等访问修饰符。 >当从内部类继承的时候内部类是不会被覆盖的,它们是完全独立的实体,每个都在自己的命名空间内,如果从内部类中明确地继承,就可以覆盖原来内部类的方法。 #进程和线程的区别 + 进程 是相对于操作系统而言的。例如你一边听歌一边玩游戏,我们就会说此时系统内有两个进程在运行,专业的话说叫做多个程序几乎在同一时间执行多个任务。 + 线程 相对某一程序而言。例如你一边听歌一边看歌曲排行榜,还可以下载歌曲,这三件事情互相不会干扰,就可以说这一音乐程序至少有三个线程在运行,专业的表述为一个程序在同一时间内执行多个任务。 #你能说出TCP/IP的七层协议吗? >应用层,、表示层、会话层、传输层、网络层、数据链路层、物理层。 #在connection类中提供了3个控制事务的方法,说说它们。 - setAutoCommit(boolean bln) 保持数据的完整性。一个系统的更新操作可能要涉及多张表,需要多个SQL语句进行操作,循环连续的进行插入操作,如果在开始时设置了”conn.setAutoCommit(false);”,然后再进行”conn.commit()”。这样即使插入的时候报错,修改的内容也不会提交到数据库,而如果没有手动进行setAutoCommit,那么出错的时候就会造成,前几条数据插入成功而后几条数据插入失败的情况,这就是脏数据。 - commit() 提交事务,用于把事务所做的修改都保存到数据库中,它把上一个commit或者rollback命令之后的全部事务都保存到数据库中。 - rollback() 撤销事务,在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已经完成的操作全部撤销,回滚到事务开始的状态,这里的操作特指数据库更新操作,并且回滚后,事务进入提交状态,因为回滚是回滚到事务开始时的状态。 #EL表达式的隐含对象 * applicationScope 应用程序范围内的scoped变量组成的集合 * cookie 所有cookie组成的集合 * header HTTP请求头部,字符串 * headerValues HTTP请求头部,字符串集合 * pageContext 当前页面的javax.servlet.jsp.PageContext对象 * initParam 全部应用程序参数名组成的集合 * pageScope 页面范围内所有对象的集合 * param 所有请求参数字符串组成的集合 * paramValues 所有作为字符串集合的请求参数 * requestScope 所有请求范围的对象的集合 * sessionScope 所有会话范围的对象集合 #简述Statement和PreparedStatement的区别 >无论多少次地使用同一SQL命令PreparedStatement都只对它解析和编译一次,当使用Statement对象时,每次执行一个SQL命令的时候都会对它解析和编译。因此使用PreparedStatement要比使用Statement速度要快。 #java中包的用途 * 允许将类文件组织起来,便于查找合适的类 * 包可以包含其他的,形成有层次的包空间 * 包有助于避免命名冲突 #请简述一下什么是流? >流是指一连串流动的字符,以先进先出的方式发送和接收数据的通道。流分为输入流和输出流,相对于内存而言,数据输入到内存就是输入流,反之就是输出流。 #java.io.reader和java.io.inputStream的区别? >两者共同组成了java输入类。Reader用于读入16位字符,即Unicode编码的字符;而InputStream用于读入ASCLL字符和二进制数据。 #请说出ArrayList和Vector的区别? - 同步性 数组序列是线程不安全也不同步的,而矢量队列则完全相反。 - 数据增长 需要增长时,数组序列增长原来的一半,而矢量队列增长一倍。 #super关键字是干什么的?为什么使用它?它的访问范围? >super用于解决如何直接访问或初始化从父类继承来的成员。 >在写子类无参数构造方法时,不用显式调用父类无参数构造方法,系统会自动提供,但在写子类带参数的构造方法时,应该在第一句写super(参数)来初始化父类成员变量 >访问范围包括父类属性、一般方法和构造方法 #Cookie和Session的区别与联系? - Session的概念 Session 是存放在服务器端的,类似于Session结构来存放用户数据。当浏览器第一次发送请求时,服务器自动生成一个Session和Session ID用来标识这个Session,并将通过相应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取Session ID并和保存的所有Session ID进行对比,找到这个用户对应的Session。一般浏览器提供了两种方式来保存Session,还有一种是程序员使用HTML隐藏域的方式自定义实现。 使用Cookie实现保存,这是最常见的方法,例如”记住我的登录状态”这一功能就是通过这种方式实现的。服务器通过设置Cookie的方式将Session ID发送到浏览器。如果我们不设置过期时间,那么Cookie将不会保存在硬盘上,会随着浏览器的关闭而消失,Session ID也就不复存在了。如果我们设置这个时间为若干天后,那么这个Cookie会保存在客户端硬盘中,即使浏览器关闭,这个值仍然存在,下次访问相应的网站时同样会发送到服务器上去。 使用URL附加信息的方式,这也是有时候我们会在JSP网站上看到”xxx.jsp?JSESSIONID=x”的原因。这种方式和使用Cookie的方式中不设置过期时间是一样的。 第三种方式是通过在页面表单里增加隐藏域的方式,这种方式实际上跟URL附加信息的方式一样,只不过前者使用GET方式发送数据,后者使用POST方式发送数据,但是明显后者比前者麻烦。 - 会话技术 会话指的是用户登录网站后的一系列操作,例如浏览商品->添加到购物车->提交订单->付款。会话跟踪时WEB程序中常用的技术,用户跟踪用户的整个会话,常用的会话跟踪技术就是Session和Cookie。Session在服务端记录信息并确认用户身份,Cookie在客户端记录信息并确认用户身份。 - 区别 Cookie存储在浏览器客户端,Session存储在服务端,简单说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,客户端每次请求服务器的时候会发送当前会话的session id,服务器根据当前session id判断相应用户数据标志,以确定用户是否登录或者具有某种权限。由于数据时存储在服务器上面,所以无法伪造,当时如果你能获取某个登录用户的session id,用特殊的浏览器伪造该用户的请求也是能够成功的。session id 是服务器和客户端链接时随机分配的,一般来说不会重复,但如果存在大量并发的请求,也不是没有重复的可能性。 - 联系 Cookie是Session的一种,但Cookie不会占用服务器资源,是存储在客户端内存或者一个cookie的文本文件中;而Session则会占用服务器资源,从这点上看,应该尽量使用Cookie而不是Session。 如果用户禁用cookie,那么可以采用url重写技术来进行页面处理,调用session中大量有用的方法从session中获取数据后置入页面。 - 应用场景 Cookie的典型应用时下次直接登录和在线商城的购物车的设计,当然这其中也会有一些安全和性能的问题存在。 由于Cookie的不安全性,所以一般会在Session上保存重要的信息。 #JSP的九大内置对象及其作用 >内置对象指的是可以不加声明和创建就可以在JSP脚本中使用的成员变量. >产生的时机:一个JSP页面对应一个Servlet类,每个Servlet有三个方法。init()初始化JSP;destory()销毁JSP;service()对用户请求产生相应的方法。Request和response是service()的形参,application、page、out、pageContext、session这些对象都是在service()中生成的实例。 * javax.servlet.httpServletRequest 客户端的请求信息被封装在request对象中,主要用于接受通过HTTP协议传送到服务器的数据,包括头信息,系统信息,请求方式以及参数等,作用域为一次请求。 * javax.servlet.httpServletResponse 代表对客户端的相应,主要是将JSP容器处理过的对象传回客户端,response对象只在JSP页面有效。 * Session Session是由服务器自动创建的与用户请求相关的对象,服务器为每个用户都生成一个session对象,用户保存用户的信息,跟踪用户的操作状态。它内部使用Map类来保存数据,即key/value,value可以是复杂的对象类型,而不仅仅局限于字符串类型。 * application Application对象可以将信息保存到服务器直到服务器关闭为止,可以认为是系统中的全局变量,是ServletContext类的实例。 * out 此对象用于在web浏览器内输出信息,并且管理应用服务器上的输出缓冲区,是JspWriter的实例。 * pageContext 此对象提供了对JSP页面内所有对象以及命名空间的访问,它的本类名也叫做pageContext。 * config 获取服务器的配置信息 * page 它是java.lang.Object的实例,代表着JSP本身,本质上包含当前Servlet接口引用的变量,可以看做Java编程中的this * exception 显示异常信息,只有在包含isErrorPage=”true”的页面上才可以使用。JAVA程序可以使用try/catch处理异常信息,但在JSP页面中出现异常就会生成exception对象,并把该对象传送到在page指令中设定的错误页面中,程序员在错误页面中处理exception对象即可。跟JAVA一样,JSP中exception对象也是由系统提供的继承机构,它实际上是java.lang.Throwable的对象。 #说一说你常用的Linux命令 * ls 显示文件或目录 -l 列出文件详细信息(list) -a 列出当前目录下所有的文件及目录,包括隐藏文件(all) * mkdir 创建目录 - p 创建目录,如果没有父目录则创建父目录(parent) * cd 切换目录 * touch 创建空文件 * echo 创建带有内容的文件 * cat 查看文件内容 * cp 拷贝 * mv 移动或者重命名 * rm 删除文件 -r 递归删除,删除文件时一并删除子目录及文件 -f 强制删除 * find 在文件系统中搜索某文件 * wc 统计文本中行数、字数、字符数 * grep 在文本文件中查找某个字符串 * rmdir 删除空目录 * tree 显示目录为树形结构,需要安装tree包 * pwd 显示当前目录 * in 创建链接文件 * more、less 分页显示文本文件内容 * head、tail 显示文件头、尾内容 * Ctrl+alt+F1 命令行全屏模式 ------系统管理命令------ * stat 显示指定文件的详细信息,比ls更详细 * who 显示在线登录用户 * whoami 显示当前操作用户 * hostname 显示主机名 * uname 显示系统信息 * top 动态显示当前耗费资源最多的进程信息 * ps 显示瞬时进程的运行状态,ps aux:以BSD格式显示进程;ps ef:以标准格式显示进程 * kill 杀死进程,一般使用ps或top命令查看进程的id,然后使用kill命令杀死对应进程。 * du 查看目录大小 -h 带有单位地显示目录信息 * df 查看磁盘大小 -h 带单位显示磁盘信息 * ifconfig 查看网络情况 * ping 测试网络连通 * netstat 显示网络状态信息 * man 帮助命令,使用-加上其他命令来查看对应参数的使用 * clear 清屏 * alias 对命令重命名 * sudo 允许系统管理员让普通用户执行一些或者root命令的工具 -b 在后台执行指令 -h 显示帮助 -H 将HOME环境变量设置为新身份的HOME环境变量 -k 结束密码的有效期,使用sudo的时候先输入密码,默认为5分钟有效期限 -l 列出目前用户可执行与无法执行的指令 -p 改变询问密码的提示符号 -s<shell> 执行指定的shell -u<用户> 以指定的用户作为新的身份,若无此参数,则默认使用root为新的身份 -v 延长密码有效期限5分钟 -V 显示版本信息 ------打包压缩相关命令------ * tar 打包压缩 -c 归档文件 -x 压缩文件 -v 显示压缩或解压缩过程(view) -f 使用档名 -z gzip压缩文件 -j bzip2压缩文件 例如 tar -cvf /home/java.tar home/java 只打包不压缩 tar -zcvf /home/java.tar home/java 打包并使用gzip压缩 ------打包压缩相关命令------ * shutdown -r 关机重启 -h 关机不重启 now 立即关机 * halt 关机 * reboot 重启 ------VIM相关命令------ Vim中有三种模式,命令模式、插入模式和编辑模式,使用ESC或i或:来切换模式。命令模式下 :q 退出 :q! 强制退出 :wq 保存并退出 :set number 显示行号 :set nonumber 隐藏行号 /java 在文档中查找java这个字符串,按n跳下一个,shift+n上一个 yyp 复制光标所在行,并粘贴 ------文件权限管理------ R 读 数值表示为4 W 写 数值表示为2 X 可执行 数值表示为1 更改权限命令语法 sudo chmod[u=所属用户/g=所属组/o=其他用户/a所有用户][+=增加权限/-=减少权限][r/w/x] 目录/文件名 #Java运行时区域 程序计数器:记录当前指令所在单元地址,也就是程序执行.class文件某行。由于存储的只是一个行数number,所以区域较小。在多线程环境中,每个线程拥有一个程序计数器,分别记录着各自程序的执行程序所在行数。 JVM栈:存放执行的方法,方法中的局部变量和对象的引用(对象的实例存放在堆中)。由于拥有着先进后出的特性,所以可以看做一个桶。对桶中数据的操作成为压栈和弹栈。 本地方法栈:存放执行的本地方法,即使用native关键字修饰的方法。当程序执行到本地方法时,程序计数器不会记录,而是使用undefined替代(undefined和null的主要区别在于null表示此处不应该有值,而undefined表示此处有值但是还没有定义) Java堆:存放类的实例和数组。分为新生代(young generation)和老年代(old generation),这样划分主要是为了方便垃圾回收器的工作,垃圾回收器会频繁地扫描新生代中的对象并进行垃圾回收,没有进行垃圾回收的对象就被移到了老年代中。 P:为了更加方便垃圾回收器的工作,JVM在新生代中又进行划分(eden和survicor)~~ 方法区:又被称为非堆,存放类的基本信息以及常量(final)和静态(static)变量,这些数据被定义为永久代,垃圾回收器不会回收这些数据。 P1:此方法区跟java中的方法没有任何关系~ P2:JDK文档中说方法区是JAVA堆逻辑上的一部分。 P3:在JDK8中取消了永久代而是使用了metespace替代 运行时常量池:存放字面量(例如int number = 10 中的10就是一个字面量)和符号引用(例如类与引用名的全限定名,List<Dept> deptList = new ArrayList<Dept>();中的deptList实际是一个java.lang.List) P1:常量池的出现是为了避免频繁的创建和销毁对象而影响到系统性能,其实现了对于对象的共享。例如字符串常量池就是在编译期间就把所有字符串文字放到一个常量池中。 P2:字符串常量池在JDK7时被移到了Java堆中(String name = “老王”中name作为引用存放到字符串常量池中,”老王”作为实例存放到堆中)。 P3:程序计数器、JVM栈和本地方法栈是线程私有的,Java堆、方法区和运行时常量池是线程共享的,因此一个多线程程序出现BUG,那么一定是线程共享中的区域出现了问题。 P4:Java规范中指出,除了程序计数器之外其他区域都有可能会发生OutOfMemoryError(内存溢出)异常。除了程序计数器和JVM栈其他区域都有可能会发生StackOverflowError(栈溢出)异常。使用JConsole(java自带)可以监控当前程序的内存和系统资源使用情况。 #常用的SQL语句 # stuscore 表中数据结构 stuid name subject score 1 张三 数学 91 1 张三 英语 89 1 张三 语文 87 2 李四 语文 89 2 李四 英语 28 2 李四 数学 99 3 王五 语文 66 3 王五 数学 24 3 王五 英语 32 4 朱六 数学 88 >计算每个人的总成绩并排名,要求显示字段:名字和总成绩 思路:根据名字分组 group by 根据成绩排名 order by SELECT name,SUM(score) FROM stuscore GROUP BY name ORDER BY SUM(score) >计算每个人的总成绩并排名,要求显示字段:名字、学号和总成绩 思路:DISTINCT 去重关键字 使用子查询先查询学号和总成绩 SELECT DISTINCT t1.name, subject,MAX(score) FROM stuscore t1, (SELECT stuid,SUM(score) AS allscore FROM stuscore GROUP BY stuid) t2 WHERE t1.stuid=t2.stuid ORDER BY t2.allscore DESC >计算每个人单科的最高成绩,要求显示字段:学号、姓名、课程、最高成绩 思路:有两种写法 #写法一 SELECT stuid,name,subject,MAX(score) FROM stuscore GROUP BY stuid #写法二 SELECT t1.stuid,t1.name,t1.subject,t1.score FROM stuscore t1, (SELECT stuid,MAX(score) AS maxscore FROM stuscore GROUP BY stuid) t2 WHERE t1.stuid=t2.stuid AND t1.score=t2.maxscore >计算每个人的平均成绩,要求显示字段:学号、成绩、平均成绩 思路:使用AVG()函数求出平均成绩,然后根据name分组 SELECT stuid,name,AVG(score) FROM stuscore GROUP BY name >列出各门课程成绩最好的学生,要求显示字段:学号、姓名、科目、成绩 #写法一,这种写法会出现问题 SELECT stuid,name,subject,MAX(score) FROM stuscore GROUP BY subject #写法二 SELECT s1.stuid,s1.name,s1.subject,s2.max_score FROM stuscore s1, (SELECT subject,MAX(score) AS max_score FROM stuscore GROUP BY subject) s2 WHERE s1.subject=s2.subject AND s1.score=s2.max_score >列出成绩最好的两位学生,要求显示字段:学号、姓名、成绩 思路:使用DESC将查询结果降序,然后使用limit截取列数 SELECT stuid,name,SUM(score) AS sumscore FROM stuscore GROUP BY name ORDER BY sumscore DESC LIMIT 2 #写法二,在SQL Server中的top关键字 SELECT top 2 * FROM stuscore ORDER BY sumscore DESC >列出数学成绩最好的学生 SELECT name,subject,score FROM stuscore WHERE subject=’数学’ ORDER BY score DESC >求出某位学生的数学成绩排名 思路:使用rownum临时变量输出mysql的排序后的行号 SELECT s.rowNo FROM (SELECT (@rowNum:=@rowNum+1) AS rowNo,name,subject,score FROM stuscore, (SELECT (@rowNum :=0)) b WHERE subject=’数学’ ORDER BY stuscore.’score’ DESC) s WHERE name=’张三’ >统计学科成绩优、良、及格的个数 SELECT subject, (SELECT COUNT(*) FROM stuscore WHERE score>80 AND score <=100) AS 优, (SELECT COUNT(*) FROM stusocre WHERE score>60 AND score <80) AS 良, (SELECT COUNT(*) FROM stuscore WHERE score<60) AS 不及格 FROM stuscore GROUP BY subject >使用SQL语句输出以下格式的数据 # 学号 姓名 语文 数学 英语 总分 平均分 SELECT stuid AS 学号,name AS 姓名, SUM(CASE WHEN subject=’语文’ THEN score ELSE 0 END) AS 语文, SUM(CASE WHEN subject=’数学’ THEN score ELSE 0 END) AS 数学, SUM(CASE WHEN subject=’英语’ THEN score ELSE 0 END) AS 英语, SUM(score) AS 总分,(SUM(score)/COUNT(*)) AS 平均分 FROM stuscore GROUP BY stuid,name >使用一条SQL语句查询出每门课都大于80的学生姓名 思路:使用HAVING()筛选成组后的各种数据,真实表中没有此数据,这些数据是通过一些函数生存。需要注意的是where子句在聚合前先筛选记录,作用于group by 和 having子句之前 SELECT name FROM stusocre GROUP BY name HAVING MIN(score)>=80 AND COUNT(subject)>=3 >修改表结构,学历 #添加列信息 Alter table stuscore add 学历 varchar(6); #删除列信息 Alter table stuscore drop column 学历; >修改表数据 朱六 #将数学成绩改为90 UPDATE stuscore SET score=90 WHERE name LIKE ‘朱六’; #删除表数据似乎不能使用like进行delete DELETE FROM stuscore WHERE stuid=4; >连接查询 # 当用户查看的数据来自于多张表的时候,连接查询会将多张表按照某个指定的条件进行记录的拼接。最终的结果是记录数可能会变化,但字段数一定会增加。 SELECT s1.*,s2.* FROM student AS s1 left join stuscore AS s2 ON s1.student_id = s2.stuscore_id #能介绍一下redis吗?在项目中你是怎么使用它的? >redis是最常用的非关系型数据库(Not-Only SQL)中的键值存储数据库(key-value),除了键值存储数据库之外还有文档型数据库(MongoDB)、图形(Graph)数据库(InfoGrid)、列存储数据库(HBase)。 对数据库高并发读写(High performance)的需求;对海量数据的高效率存储访问(Huge Storage)的需求;对数据库的高可扩展性(High Scalability)和高可用性( High Availability)的需求,带动了NOSQL的发展。 >redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,类似于memcached(比之出色的地方在redis的value的最大限制是1GB,而memcached只有1MB),它通过异步的方式将整个数据库加载到内存中然后进行操作,因为是纯内存操作,所以性能很好。不仅仅是出色的性能,它还通过提供多种键值数据类型来适应不同场景下的存储需求。 - 字符串类型 redis没有采用C语言对字符串的处理方式,而是自定义了一个数据结构SDS(imple dynamic string),简单动态字符串。打开redis源码包,在src下的sds.h文件下查看sds的源码如下: struct sdshdr { // 字符串长度 unsigned int len; // buf数组中未使用的字节数量 unsigned int free; // 用于保存字符串 char buf[]; }; >C语言对字符串的存储是使用字符数组,遇到'\0'字符则认为字符串结束,而redis的字符串可以存储任何类型的数据,因为任何类型数据都可以表示成二进制,sds结构中的char buf[]就是存储了二进制数据。并且redis的字符串是二进制安全的,什么是二进制安全?简单理解就是存入什么数据取出的还是什么数据。redis中的sds不像c语言处理字符串那样遇到'\0'字符则认证字符串结束,它不会对存储进去的二进制数据进行处理,存入什么数据取出还是什么数据。 >应用:自增主键,例如电商项目中的订单号和商品编号都是采用String的递增数字特性生成,使用INCR(此命令用于将key中存储的数字值增1)命令完成。 定义商品编号key:items:id 192.168.101.3:7003> INCR items:id (integer) 2 192.168.101.3:7003> INCR items:id (integer) 3 散列类型 hash 如果有一个对象User以JSON序列化(String)的形式存储在redis,它的存储过程是User->JSON->Redis。该User对象有id、name、age等属性。如果在业务中只需要修改name属性,其他属性不做修改时该怎么做?如果采用传统的存储过程时必然会导致资源的的浪费,使用hash可以解决此问题。 hash会怎么做?它会提供字段与字段值的映射,并且规定字段值只能是字符串不能是散列或者集合类型。使用HSET(此命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0)命令对字段值做修改: HSET user name laowang 应用:商品信息,包括商品id、商品名称、商品描述、商品库存、商品好评 商品信息在redis中为item:1001 获取命令 HGET items:1001 id "3" HGETALL items:1001 1) "id" 2) "3" 3) "name" 4) "apple" 5) "price" 6) "999.9" 列表类型 -arrayList && linkedList ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。LinkedList使用双向链接(AB互相指向)方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。 -List 列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1)(算法中的时间复杂度,它和空间复杂度被合称为算法的复杂度,是衡量算法重要指标),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。 应用,商品评论列表 思路:在redis中创建商品评论列表,当用户发布商品评论时,将评论信息转成json存储到list中,用户在页面查询评论列表,从redis中取出json数据展示到页面。 定义商品评论列表key: 商品编号为1001的商品评论key:items: comment:1001 LPUSH items:comment:1001 '{"id":1,"name":"色情买家举报了!!!","date":20180510}' 集合类型 -set 集合的含义是每个元素的顺序不同,且没有顺序。集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。 Redis还提供了多个集合之间的交集、并集、差集的运算。 有序集合类型 - 在集合类型的基础上有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。 在某些方面有序集合和列表类型有些相似。 1、二者都是有序的。 2、二者都可以获得某一范围的元素。 但是,二者有着很大区别: 1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。 2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。 3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现) 4、有序集合要比列表类型更耗内存。 应用:商品销售排行榜 根据商品销售量对商品进行排行显示,定义sorted set集合,商品销售量为元素的分数。 定义商品销售排行榜key:items:sellsort 写入商品销售量: 商品编号1001的销量是9,商品编号1002的销量是10 ZADD items:sellsort 9 1001 10 1002 商品编号1001的销量加1 ZINCRBY items:sellsort 1 1001 商品销量前10名: ZRANGE items:sellsort 0 9 withscores redis的主要缺点在于数据库容量受到物理内存的限制不能用作海量数据的高性能读写(意思就是要考虑那些低内存的用户电脑),因此redis最合适的使用场景是那些较小数据量的高性能操作和运算上,例如: 缓存(数据查询、短连接、新闻内容、商品内容等等)(最多使用) 分布式集群架构中的session分离。 聊天室的在线好友列表。 任务队列(秒杀、抢购、12306等等) 应用排行榜。 网站访问统计。 数据过期处理(可以精确到毫秒) redis不仅仅只能用命令的形式操作,主流的后端语言都有对应的客户端支持,java官方推荐使用jedis和redisson。jedis托管在github上,你可以通过https://github.com/xetorthio/jedis访问和下载jedis客户端。 Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。 RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。RDB是Redis默认采用的持久化方式。Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。 P:通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。 默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:appendonly yes。开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改:appendfilename appendonly.aof 为了保证redis不会因为重启等原因而导致数据丢失,redis提供了持久化的解决方案,redis服务重新启动后会将硬盘中的数据恢复到内存中,但是此过程中如果redis服务器的硬盘损坏的话可能就会导致数据丢失。为了更好的保证数据的完整性,避免这种单点故障,可以使用redis提供了主从复制机制。主redis(master)中的数据有两个副本(replication)即从redis1(slave)和从redis2(slave),即使一台redis服务器宕机其它两台redis服务也可以继续提供服务 redis中常见的性能问题和解决方案 master写内存快照(snapshotting),sava命令调度rdbSava函数会阻塞主线程的工作,当快照比较大时对性能影响非常大,会间断性暂停服务,所以master最好不要写内存快照。 master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响master重启的恢复速度。master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。 master调用BGREWRITEAOF(redis中的一个命令,作用是异步执行一个AOF文件重写操作。重写会创建一个当前AOF文件的体积优化版本,即使BGREWRITEAOF执行失败,也不会有任何数据丢失,旧的AOF文件在BGREWRITEAOF操作成功之前不会被修改。从redis2.4开始,AOF重写由redis自行触发,BGREWRITEAOF仅用于手动触发重写操作。)重写AOP文件,在AOP重写的时候会大量的占用CPU和内存资源,导致服务load过高,出现短暂暂停现象。 redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网下。 MySQL中有2000W数据,redis中只存了20W数据,那么如何保证reids中的数据都是热点数据。 redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略),redis提供了6种数据淘汰策略。 volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任何选择数据淘汰。allkeys-lru:从数据集(server.db(i).dict)中挑选最近最少使用的数据淘汰。allkeys-random:从数据集(server.db(i).dict)中任何选择数据淘汰。no-enviction(驱逐):禁止驱逐数据。 什么是spring框架,它有哪些模块,使用它有什么好处。 Spring框架是一个为Java应用程序的开发提供了综合、广泛的基础性支持的java平台,也是一个实现IOC和AOP的容器框架,设计理念就是简化开发。 spring框架集成了许多模块,常用的有spring ioc、spring aop,spring mvc、spring boot、spring security。 使用spring的好处 降低开发组件之间的耦合度,实现软件各层的解耦。 spring提供DI spring通过AOP技术完成了对事务的管理,使得开发人员不再需要手工控制事务,也不需要处理复杂的事务传播。同时提供的AOP技术可以很容易实现比如权限拦截、运行期监控、日志管理等功能 在Hibernate中管理事务 public void save(){ Session session = sessionFactory.getCurrentSession(); Session.beginTransaction(); Info info = new Info(“百度”); Info.setContent(“一家良心企业”); session.save(); session.getTransaction().commit(); } 在JDBC中管理事务 Connection conn = null; try{ conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.executeUpdate(update person where name=”老王”) conn.commit(); } catch(Exception e){ } final {conn.close()} 在spring中管理事务 @Transactional public void save(){ sessionFactory.getCurrentSession.svae(info); } 提供了很多辅助类,比如JdbcTemplate、HibernateTemplate,用于加快应用的开发 spring容器提供了单例支持(spring配置文件中定义的bean默认为单例模式),开发人员不需要编写代码来实现单例。 spring对于主流的框架,比如MyBatis、Hibernate提供了集成支持。 相对于其他集成框架,比如说EJB,spring IoC更加轻量级,使其在有限的内存和CPU资源下可以进行应用程序的开发和发布 请你谈一谈Spring的IOC和AOP? IOC是一种设计模式的实现,有两层实现。 对象的创建都不再由程序员控制,而是交由Spring控制,称之为控制反转。 对象的依赖关系由Spring的配置文件描述,并且这种关系只有在使用的时候才会建立,称之为依赖注入。 spring中的org.springfremework.beans包和org.springfreamework.context包构成了spring框架的IoC容器的基础,org.springframework.beans.factory.BeanFactory是spring IoC容器的具体实现,用于包装和管理各种bean,因此该接口是spring IoC的核心接口。ApplicationContext接口对BeanFactory进行了扩展,提供了message resource用于国际化的机制、事务传播和应用层的特别配置,比如针对WEB应用的WebApplicationContext。 AOP是一种编程思想,叫做面向切面,是OOP的延续和补充。P:这种在运行时,动态地将代码切入到类的指定方法,指定位置上的编程思想就是面向切面的编程。 在Spring中的具体实现是将系统中的非核心业务基于代理机制做提取,进行单独处理。比如事务,比如日志,比如安全授权等。 spring框架中大量使用了设计模式,请列举几个 代理模式 Spring AOP就是采用动态代理实现的,spring中有两种动态代理方式,jdk代理和cglib代理。 单例模式 Spring配置文件中的bean默认为单例 模版方法 Spring提供了许多模板类用于解决代码的重复问题,例如RestTemplate、JmsTemplate、JpaTemplate 前端控制器 Spring 提供给了DispatcherServlet来对请求进行分发 视图帮助 Spring提供了一系列的JSP标签,高效宏来辅助视图 工厂模式 使用BeanFactory创建对象实例

优秀的个人博客,低调大师

整理位运算

参考链接(总结的非常好,各种奇淫技巧):https://blog.csdn.net/MoreWindows/article/details/7354571 1、优先级 “<<” 和 ">>" 的优先级低于 “+” 和 “-”。 “~” 按位求反 “^”按位异或 “|”按位求或 “&”按位求与 2、基本概念 注意以下几点: 1.在这6种操作符,只有~取反是单目操作符,其它5种都是双目操作符。 2.位操作只能用于整形数据,对float和double类型进行位操作会被编译器报错。 3.15=0000 1111(二进制),右移二位,最高位由符号位填充将得到0000 0011即3。 -15 = 1111 0001(二进制),右移二位,最高位由符号位填充将得到1111 1100即-4。 https://blog.csdn.net/studyvcmfc/article/details/7606292 https://jingyan.baidu.com/article/29697b9106eb52ab21de3c7a.html ———————————————————————————————————————————————————— 看到《c++ primer》里面的内容了,再来补充一下 2018.11.20 1、移位运算符 运算符右侧的运算对象一定不能为负,而且值必须严格小于结果的位数,否则会产生未定义的行为。 左移运算符(<<)在右侧插入值为0的二进制位。 右移运算符(>>)的行为依赖于其左侧运算符对象的类型: 如果该运算对象是无符号类型,则在左侧插入值为0的二进制位; 如果该运算对象是带符号类型,在左侧插入符号位的副本或值为 0 的二进制,如何选择,要视具体环境而定。 未完待续——————————————————————————————————————————————

优秀的个人博客,低调大师

Java并发整理

读Java并发专题总结 一. 基础知识 新建线程 继承Thread类,重写run方法 实现Runable接口 实现Callable接口 线程状态 NEW/Runable/BLOCKED/TIMED _WAITING/WAITING/TERMINATED 调用wait()、join()、LockSupport.lock()方法, 线程会进入到WAITING状态 调用带超时时间的wait(long timeout)、sleep(long)、join(long)、LockSupport.parkNanos()、LockSupport.parkUtil()方法, 线程会进入到TIMED_WAITING状态 调用Object.notify()、Object.notifyAll()方法使线程转换到Runable状态 进入synchronized方法或者synchronized代码块时,线程切换BLOCKED状态 与WAITING状态相关联的是等待队列,与BLOCKED状态相关的是同步队列 线程协作 join()、join(long millis) : 一个线程等待另一个线程结束 sleep()与wait() sleep()方法是Thread的静态方法,而wait是Object实例方法 sleep()方法并不会失去锁, 不会释放资源,只会释放CPU. wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源. wait()方法必须等待notify()/notifyAll()通知后, 才会离开等待池 yield() 让出CPU, 让出的时间片只会分配给当前线程相同优先级的线程, 本线程也会继续参数CPU竞争 二. 两大核心&三个性质 并发理论JMM 内存一致性 共享内存、CPU缓存 重排序 编译器优化重排序 指令重排序 内存系统重排序 happens-before原则 原子性 一个操作是不可中断的,要么全部执行成功要么全部执行失败 lock(锁定) unlock(解锁) read(读取) load(载入) use(使用) assign(赋值) store(存储) write(操作) i++不是原子操作 : 1.读取变量 2.值加一 3.赋值回变量 有序性 synchronized具有有序性 volatile包含禁止指令重排序的语义,其具有有序性 可见性 synchronized和volatile都具有可见性 三. 并发关键字 synchronized关键字 类的实例对象锁 类本身对象锁 对象锁(monitor)机制 每个对象都有一个监视器(monitor)&计数器 锁具有重入性 CAS锁优化 ABA问题 : 添加版本号解决比较值相等问题 自旋时间过长问题(线程不挂起,死循环) : 非阻塞同步, 线程不会挂起, 死循环 Java对象头 对象是否有锁, 有(轻量级、重量级、偏向锁)标志位 volatile关键字 修饰的共享变量 : 会有Lock前缀的指令 缓存一致性 Lock前缀的指令会引起处理器缓存写回内存 一个处理器的缓存回写到内存会导致其他处理器的缓存失效 当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值 内存屏障 为了实现volatile内存语义时,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序 保证可见性,不保证原子性 final关键字 可以修饰变量、方法、类 修饰基本数据类型变量时,不能对基本数据类型变量重新赋值,因此基本数据类型变量不能被改变 修饰引用类型变量时, 它仅仅保存的是一个引用, final只保证这个引用类型变量所引用的地址不会发生改变,即一直引用这个对象,但这个对象属性是可以改变的 父类的方法被final修饰的时候,子类不能重写父类的该方法 final方法是可以被重载的 当一个类被final修饰时, 说明该类是不能被子类继承的 JDK中提供的八个包装类和String类都是不可变类 写final域重排序规则 禁止对final域的写重排序到构造函数之外 编译器会在final域写之后, 函数return之前,插入一个storestore屏障,这个屏障可以禁止处理器把final域的写重排序到构造函数之外 在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域就不具有这个保障 读final域重排序规则 在一个线程中, 初次读对象引用和初次读该对象包含的final域, JMM会禁止这两个操作的重排序(仅对处理器生效) 在读一个对象的final域之前,一定会先读这个包含这个final域的对象的引用 四. Lock体系 AbstractQueuedSynchronizer(AQS) 独占锁 共享锁 ReentrantLock重入锁 公平锁 满足FIFO,锁的获取顺序符合请求上的绝对时间顺序 频繁切换上下文, 性能低 非公平锁 会造成永远抢不到锁的情况,饥饿 ReentrantLock默认是非公平锁,保证吞吐量 ReentrantReadWriteLock读写锁 允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞 锁降级:遵循获取写锁,获取读锁再释放写锁的次序, 写锁能够降级成读锁 Condition 等待/通知机制 线程中断 InterruptedException : 当调用Thread.interrupt()中断一个线程时. 若线程处于阻塞中(Thread.sleep()、Thread.join()、Object.wait()), 线程会取消阻塞抛出异常 LockSupport 线程的阻塞原语,用来阻塞线程和唤醒线程 每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在线程中使用,则调用park()将会立即返回,否则可能阻塞' 如果许可尚不可用,则可以调用unpark()使其可用 许可不可以重入 void park():阻塞当前线程 void unpark(Thread thread):唤醒处于阻塞状态的指定线程 synchronzed致使线程阻塞,线程会进入到BLOCKED状态 LockSupprt方法阻塞线程会致使线程进入到WAITING状态 五. 并发容器 ConcurrentHashMap 利用了锁分段的思想提高了并发度 JDK1.8之前 segment继承了ReentrantLock充当锁的角色,为每一个segment提供了线程安全的保障 segment维护了哈希散列表的若干个桶,每个桶由HashEntry构成的链表 JDK1.8之后 舍弃segment,大量使用synchronized以及CAS synchronized优化: 偏向锁、轻量级锁、重量级锁、锁状态升级 底层数据结构 : 数组 + 链表 + 红黑树 构造方法支持传入(Map的大小、加载因子、并发度) spread()重哈希 : hash值分散的不够均匀的话会大大增加哈希冲突的概率,从而影响到hash表的性能.通过spread方法进行了一次重hash从而大大减小哈希冲突的可能性. (h ^ (h >>> 16)) & HASH_BITS : 将key的hashCode的低16位于高16位进行异或运算 CopyOnWriteArrayList 线程安全,读读之间不会被阻塞 牺牲数据实时性满足数据的最终一致性即可 COW : 写时复制的思想来通过延时更新的策略来实现数据的最终一致性,并且能够保证读线程间不阻塞 ReentrantLock : 同一时刻只有一个写线程正在进行数组的复制 数组引用是volatile修饰的,因此将旧的数组引用指向新的数组,根据volatile的happens-before规则,写线程对数组引用的修改对读线程是可见的 volatile的修饰的仅仅只是数组引用,数组中的元素的修改是不能保证可见性的 适合写少读多的情况,每次写的时候内存占用双份. 只保证最终一致性, 适用于不实时的配置读取等 ConcurrentLinkedQueue offer 若tail指向的节点的下一个节点(next域)为null的话,说明tail指向的节点即为队列真正的队尾节点,因此可以通过casNext插入当前待插入的节点,但此时tail并未变化 若tail指向的节点的下一个节点(next域)不为null的话,说明tail指向的节点不是队列的真正队尾节点.通过q(Node<E> q = p.next)指针往前递进去找到队尾节点,然后通过casNext插入当前待插入的节点,并通过casTail方式更改tail poll 如果当前head,h和p指向的节点的Item不为null的话,说明该节点即为真正的队头节点(待删除节点),只需要通过casItem方法将item域设置为null,然后将原来的item直接返回即可 如果当前head,h和p指向的节点的item为null的话,则说明该节点不是真正的待删除节点,那么应该做的就是寻找item不为null的节点.通过让q指向p的下一个节点(q = p.next)进行试探,若找到则通过updateHead方法更新head指向的节点以及构造哨兵节点(通过updateHead方法的h.lazySetNext(h)) 延迟更新 tail : 当tail指向的节点的下一个节点不为null的时候,会执行定位队列真正的队尾节点的操作,找到队尾节点后完成插入之后才会通过casTail进行tail更新. 当tail指向的节点的下一个节点为null的时候,只插入节点不更新tail head : 当head指向的节点的item域为null的时候,会执行定位队列真正的队头节点的操作,找到队头节点后完成删除之后才会通过updateHead进行head更新. 当head指向的节点的item域不为null的时候,只删除节点不更新head ThreadLocal 存放在ThreadLocalMap, key = Thread.currentThread() Entry是一个以ThreadLocal为key,Object为value的键值对,另外需要注意的是这里的threadLocal是弱引用 当threadLocal外部强引用被置为null(threadLocalInstance=null),那么系统GC的时候,根据可达性分析,这个threadLocal实例就没有任何一条链路能够引用到它. 这个ThreadLocal势必会被回收,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value. 如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏 set方法中会使用开放地址法向散列表存储,并且会替换key=null的脏entry,以及插入后清除key=null的脏entry.并且大于阈值就扩容 使用场景 每个不同的线程都拥有专属于自己的数据容器(threadLocalMap),彼此不影响 适用于共享对象会造成线程安全的业务场景 BlockingQueue ArrayBlockingQueue 由数组实现的有界阻塞队列,FIFO(先进先出) 容量不可改变 默认不保证线程的公平性 保证线程安全:ReentrantLock lock LinkedBlockingQueue 由链表实现的有界阻塞队列,FIFO(先进先出) 吞吐量比较大 插入数据和删除数据时分别是由两个不同的lock(takeLock和putLock)来控制线程安全的 PriorityBlockingQueue 支持优先级的无界阻塞队列 SynchronousQueue 每个插入操作必须等待另一个线程进行相应的删除操作 当前有线程在插入数据时,线程才能删除数据 LinkedTransferQueue 由链表数据结构构成的无界阻塞队列 实现了TransferQueue接口 transfer(E e) 如果当前有线程(消费者)正在调用take()方法或者可延时的poll()方法进行消费数据时,生产者线程可以调用transfer方法将数据传递给消费者线程. 如果当前没有消费者线程的话,生产者线程就会将数据插入到队尾,直到有消费者能够进行消费才能退出 LinkedBlockingDeque 基于链表数据结构的有界阻塞双端队列 DelayQueue 存放实现Delayed接口的数据的无界阻塞队列 只有当数据对象的延时时间达到时才能插入到队列进行存储 六. 线程池 ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) corePoolSize : 核心线程池 maximumPoolSize : 线程池能创建线程的最大个数.当阻塞队列已满时,并且当前线程池线程个数没有超过maximumPoolSize的话,就会创建新的线程来执行任务 keepAliveTime : 空闲线程存活时间.当前线程池的线程个数已经超过了corePoolSize,并且线程空闲时间超过了keepAliveTime的话,就会将这些空闲线程销毁 unit : 时间单位 workQueue : 阻塞队列.可以使用ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue threadFactory : 创建线程的工厂类.设置线程池名称 handler : 饱和策略 AbortPolicy : 直接拒绝提交的任务,抛出RejectedExecutionException异常 CallerRunsPolicy : 使用调用者所在的线程来执行任务 DiscardPolicy : 直接丢弃任务 DiscardOldestPolicy : 丢弃阻塞队列中存放时间最久的任务,执行当前任务 线程池合理配置 任务的性质: CPU密集型任务 : 尽量少的数量,CPU(Runtime.getRuntime().availableProcessors()) + 1 IO密集型任务 : 需要等待IO操作,数量尽可能多 任务的优先级:高、中、低 任务的执行时间 任务的依赖性:是否依赖其他系统资源,如数据库连接 最好采用有界队列,防止内存资源过多 ScheduledThreadPoolExecutor 在给定延时后执行异步任务或者周期性执行任务 DelayedWorkQueue 采用数组构成,基于堆的数据结构,插入和删除最坏时间复杂度O(logN) 按照执行时间的升序排序, 是一个优先级队列 七. 原子操作类 悲观锁 synchronized 乐观锁 CAS(compare and swap) AtomicBoolean:以原子更新的方式更新boolean AtomicInteger:以原子更新的方式更新Integer AtomicLong:以原子更新的方式更新Long AtomicIntegerArray:原子更新整型数组中的元素 AtomicLongArray:原子更新长整型数组中的元素 AtomicReferenceArray:原子更新引用类型数组中的元素 AtomicReference:原子更新引用类型 AtomicReferenceFieldUpdater:原子更新引用类型里的字段 AtomicMarkableReference:原子更新带有标记位的引用类型 AtomicIntegeFieldUpdater:原子更新整型字段类 AtomicLongFieldUpdater:原子更新长整型字段类 AtomicStampedReference:原子更新引用类型,带版本号 八. 并发工具 倒计时器CountDownLatch CountDownLatch.countDown方法时就会对计数器的值减一, 线程不会阻塞 await() : 调用该方法的线程等到构造方法传入的N减到0的时候,才能继续往下执行 一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行 不能复用 循环栅栏CyclicBarrier CyclicBarrier在使用一次后,下面依然有效,可以继续当做计数器使用,这是与CountDownLatch的区别之一(可以复用) 调用await(),阻塞等待barrier等于指定数值 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行 控制资源并发访问Semaphore(信号量) acquire() : 获取许可,如果无法获取到,则阻塞等待直至能够获取为止 tryAcquire() : 尝试获取许可,如果能够获取成功则立即返回true.否则,则返回false 默认非公平锁 用来做特殊资源的并发访问控制是相当合适的 线程间交换数据的工具Exchanger exchange(V x) : 当一个线程执行该方法的时,会等待另一个线程也执行该方法,因此两个线程就都达到了同步点. 将数据交换给另一个线程,同时返回获取的数据

优秀的个人博客,低调大师

docker 命令整理

安装Docker: ubuntu安装:curl -s https://get.docker.io/ubuntu/ | sudo sh 镜像管理 docker images:列出本地所有镜像 docker search <IMAGE_ID/NAME>:查找image docker pull <IMAGE_ID>:下载image docker push <IMAGE_ID>:上传image docker rmi <IMAGE_ID>:删除image 容器管理 docker run -i -t <IMAGE_ID> /bin/bash:-i:标准输入给容器 -t:分配一个虚拟终端 /bin/bash:执行bash脚本 -d:以守护进程方式运行(后台) -P:默认匹配docker容器的5000端口号到宿主机的49153 to 65535端口 -p <HOT_PORT>:<CONTAINER_PORT>:指定端口号 - -name: 指定容器的名称 - -rm:退出时删除容器 docker stop <CONTAINER_ID>:停止container docker start <CONTAINER_ID>:重新启动container docker ps - Lists containers. -l:显示最后启动的容器 -a:同时显示停止的容器,默认只显示启动状态 docker attach <CONTAINER_ID> 连接到启动的容器 docker logs <CONTAINER_ID> : 输出容器日志 -f:实时输出 docker cp <CONTAINER_ID>:path hostpath:复制容器内的文件到宿主机目录上 docker rm <CONTAINER_ID>:删除container docker rm `docker ps -a -q`:删除所有容器 docker kill `docker ps -q` docker rmi `docker images -q -a` docker wait <CONTAINER_ID>:阻塞对容器的其他调用方法,直到容器停止后退出 docker top <CONTAINER_ID>:查看容器中运行的进程 docker diff <CONTAINER_ID>:查看容器中的变化 docker inspect <CONTAINER_ID>:查看容器详细信息(输出为Json) -f:查找特定信息,如docker inspect -f '` `.`NetworkSettings`.`IPAddress `' docker commit -m "comment" -a "author" <CONTAINER_ID> ouruser/imagename:tag docker extc -it <CONTAINER> <COMMAND>:在容器里执行命令,并输出结果 网络管理 docker run -P:随机分配端口号 docker run -p 5000:5000:绑定特定端口号(主机的所有网络接口的5000端口均绑定容器的5000端口) docker run -p 127.0.0.1:5000:5000:绑定主机的特定接口的端口号 docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py:绑定udp端口号 docker port <CONTAINER_ID> 5000:查看容器的5000端口对应本地机器的IP和端口号 使用Docker Linking连接容器: Docker为源容器和接收容器创建一个安全的通道,容器之间不需要暴露端口,接收的容器可以访问源容器的数据 docker run -d -P --name <CONTAINER_NAME> --link <CONTAINER_NAME_TO_LINK>:<ALIAS> 数据管理 Data Volumes:volume是在一个或多个容器里指定的特殊目录 数据卷可以在容器间共享和重复使用 可以直接修改容器卷的数据 容器卷里的数据不会被包含到镜像中 容器卷保持到没有容器再使用它 可以在容器启动的时候添加-v参数指定容器卷,也可以在Dockerfile里用VOLUMN命令添加 docker run -d -P --name web -v /webapp training/webapp python app.py 也可以将容器卷挂载到宿主机目录或宿主机的文件上,<容器目录或文件>的内容会被替换为<宿主机目录或文件>的内容,默认容器对这个目录有可读写权限 docker run -d -P --name web -v <宿主机目录>:<容器目录> training/webapp python app.py 可以通过指定ro,将权限改为只读 docker run -d -P --name web -v <宿主机目录>:<容器目录>:ro training/webapp python app.py 在一个容器创建容器卷后,其他容器便可以通过--volumes-from共享这个容器卷数据,如下: docker run -d -v /dbdata --name db1 training/postgres echo Data-only container for postgres 首先启动了一个容器,并为这个容器增加一个数据卷/dbdata,然后启动另一个容器,共享这个数据卷 docker run -d --volumes-from db1 --name db2 training/postgres 此时db2使用了db1的容器卷,当容器db1被删除时,容器卷也不会被删除,只有所有容器不再使用此容器卷时,才会被删除 docker rm -v:删除容器卷 除了共享数据外,容器卷另一个作用是用来备份、恢复和迁移数据 docker run --volumes-from db1 -v /home/backup:/backup ubuntu tar cvf /backup/backup.tar /dbdata 启动一个容器数据卷使用db1容器的数据卷,同时新建立一个数据卷指向宿主机目录/home/backup,将/dbdata目录的数据压缩为/backup/backup.tar docker run -v /dbdata --name dbdata2 ubuntu /bin/bash docker run --volumes-from dbdata2 -v /home/backup:/backup busybox tar xvf /backup/backup.tar 启动一个容器,同时把backup.tar的内容解压到容器的backup 仓库管理 docker login:登录 保存对容器的修改(commit) 当你对某一个容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次可以从保存后的最新状态运行该容器。 ? 1 2 # 保存对容器的修改; -a, --author="" Author; -m, --message="" Commit message $docker commit ID new_image_name # 列出一个容器里面被改变的文件或者目录,list列表会显示出三种事件,A 增加的,D 删除的,C 被改变的 $docker diff Name/ID # 显示一个运行的容器里面的进程信息 $docker top Name/ID # 从容器里面拷贝文件/目录到本地一个路径 $docker cp Name:/container_path to_path $docker cp ID:/container_path to_path # 重启一个正在运行的容器; -t, --time=10 Number of seconds to try to stop for before killing the container, Default=10 $docker restart Name/ID 保存和加载镜像(save、load) 当需要把一台机器上的镜像迁移到另一台机器的时候,需要保存镜像与加载镜像。 # 保存镜像到一个tar包; -o, --output="" Write to an file $docker save image_name -o file_path # 加载一个tar包格式的镜像; -i, --input="" Read from a tar archive file $docker load -i file_path # 机器a $docker save image_name > /home/save.tar # 使用scp将save.tar拷到机器b上,然后: $docker load < /home/save.tar 登录registry server(login) ? 1 2 # 登陆registry server; -e, --email="" Email; -p, --password="" Password; -u, --username="" Username $docker login 详解修改docker时区及docker常用命令 docker cp /etc/localtime:【容器ID或者NAME】/etc/localtime apk add tzdata ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime echo "Asia/Shanghai" > /etc/timezone ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 本文转自 zbill 51CTO博客,原文链接:http://blog.51cto.com/dek701/1979912,如需转载请自行联系原作者

优秀的个人博客,低调大师

技术书单整理

算法 算法导论Introduction to Algorithms, Second Edition, by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein 算法概论 Algorithms, S. Dasgupta, C. H. Papadimitriou, and U. V. Vazirani Python Algorithms-Mastering Basic Algorithms in the Python Language, Magnus Lie Hetland Data Streams Models and Algorithms, Charu C. Aggarwal Web Data Mining 集体智慧编程 Programming Collective Intelligence, by Toby Segaran Mining of Massive Datasets, by Anand Rajaraman, Jeffrey D. Ullman Algorithms of the Intelligent Web, by HARALAMBOS MARMANIS DMITRY BABENKO Mining the Social Web, by Matthew A. Russell 21 Recipes for Mining Twitter, by Matthew A. Russell Machine Learning 统计学习方法, by 李航 机器学习实战 Machine Learning in Action, by PETER HARRINGTON Hadoop Ecosystem Hadoop: The Definitive Guide, by Tom White Data-Intensive Text Processing with MapReduce, by Jimmy Lin and Chris Dyer Pro Hadoop, by Jason Venner Hadoop in Action, by Chuck Lam Programming Pig, by Alan Gates Mahout in Action, by SEAN OWEN ROBIN ANIL TED DUNNING ELLEN FRIEDMAN NoSQL NoSQL Databases, by Christof Strauch HBase: The Definitive Guide, by Lars George MongoDB: The Definitive Guide, by Kristina Chodorow and Michael Dirolf Cassandra: The Definitive Guide, by Eben Hewitt MongoDB and Python, by Niall O’Higgins Redis Cookbook, by Tiago Macedo and Fred Oliveira 统计,R语言 R 导论, by W. N. Venables, D. M. Smith R for Beginners, by Emmanuel Paradis, 中文版 A Handbook of Statistical Analyses Using R, by Brian S. Everitt Torsten Hothorn Data Mining with R:learning by case studies, by Luis Torgo Statistics with R, by Vincent Zoonekynd Using R for Data Analysis and Graphics, by J H Maindonald Search Engine 深入搜索引擎 Managing Gigabytes: Compressing and Indexing Documents and Images, by ian H. Witten / Alistair Moffat / Timothy C. Bell Lucene in Action, by Erik Hatcher NLP Natural Language Processing with Python, by Steven Bird, Ewan Klein, and Edward Loper Python Text Processing with NLTK 2.0 Cookbook, by Jacob Perkins Semantic Web A Semantic Web Primer, by Grigoris Antoniou and Frank van Harmelen Programming the Semantic Web, by Toby Segaran, Colin Evans, and Jamie Taylor Web Service On Designing and Deploying Internet-Scale Services, James Hamilton RESTful Web Services, by Leonard Richardson and Sam Ruby 编码-C/C++ C专家编程,徐波译 Expert C Programming: Deep C Secrets, Peter Van Der Linden 编程精粹-Microsoft 编写优质无错C 程序秘诀 Writing Clean Code-Microsoft Techniques for Developing Bug-free C Programs, Steve Maguire C++ Primer, Fourth Edition By Stanley B. Lippman, Josée Lajoie, Barbara E. Moo Effective C++ Third Edition 深度探索c++对象模型, 侯捷译 Inside the C++ Object Model, By Stanley B. Lippman 编码-Java Java编程思想 Thinking in Java, by Bruce Eckel Effective Java™, by Joshua Bloch JAVA并发编程实践 Java Concurrency in Practice, ByBrian Goetz,Tim Peierls,Joshua Bloch,Joseph Bowbeer,David Holmes,Doug Lea Java NIO, by Ron Hitchens Java Reflection in Action, by IRA R. FORMAN, NATE FORMAN 编码-Python Dive into Python Python 核心编程 Core Python programming, by Wesley J.Chun Python Cookbook, ByDavid Ascher,Alex Martelli,Anna Ravenscroft Python in a Nutshell, ByAlex Martelli Twisted Network Programming Essentials, ByAbe Fettig 编码-Clojure Programming Clojure, by Stuart Halloway Practical Clojure, by Luke VanderHart, Stuart Sierra Clojure Programming,by Chas Emerick, Brian Carper, and Christophe Grand The Joy of Clojure, by MICHAEL FOGUS, CHRIS HOUSER Clojure in Action, by AMIT RATHORE Common Lisp 高级编程技术 On Lisp, by Paul Graham 编码-Scala Scala编程 Programming in Scala, by Martin Odersky, Lex Spoon, Bill Venners Effective Akka, by Jamie Allen Akka Essentials, by Munish K. Gupta 本文章摘自博客园,原文发布日期: 2014-04-02

优秀的个人博客,低调大师

收集整理

内容横跨Cocoa/FreeBSD/AI/NLP/LLVM,etc. 简单的说就是我个人感兴趣的方向+没玩过但是觉得很酷的方向。 当作自娱自乐也好,学习笔记也好。 iOS开发什么的,已经不重要了,因为已经不再有app,值得去开发了 欢迎贡献 也可以关注我的Github github.com/Naville 信息流里会多出来很多有价值的信息 : ) 没人Care微信助手/UIKit 英语是世界通用语言,虽然这么说但考虑到实际情况我为每条都提供了简单的描述,不过也就仅限于次了 O-oooooooooo AAAAE-A-A-I-A-U- JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA Reagan的问题 OS X如何Dump KC 现成工具https://github.com/juuso/keychaindump OS X用 https://github.com/ptoomey3/Keychain-Dumper/iOS用 OS X下还可以用其他方案,具体参见StackExchange原帖 apple.stackexchange.com http://apple.stackexchange.com/questions/184897/how-to-dump-the-content-of-keychain-from-the-shell HackingTeam入侵被过程的完整解析 http://pastebin.com/raw/0SNSvyjJ OC模块的LLDB调试辅助 https://github.com/ddeville/block-lldb-script 让LLDB资次在Xcode中中显示图片 https://github.com/ryanolsonk/LLDB-QuickLook 斯威夫特人工智能的库 https://github.com/KevinCoble/AIToolbox CVE-2016至1757年 https://github.com/gdbinit/mach_race 更改越狱的iOS设备的UDID https://github.com/iMokhles/iMoUDIDChanger BH上阿里的纸业关于起毛由于IOKit的 https://www.blackhat.com/docs/us-15/materials/us-15-Lei-Optimized-Fuzzing-IOKit-In-iOS.pdf 编译器,编译器永远不会改变任何东西 http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15745-s14/public/lectures/ 斯威夫特的逆向指南,原先我只知道还原函数 http://infiltratecon.com/archives/swift_Ryan_Stortz.pdf mobileprovision的信息QuickLook的插件 https://github.com/chockenberry/Provisioning 查看swift的类hopper插件还蛮不错哒 https://github.com/keith/hopper-swift-demangle 狄奥的售后服务,XXX级的错误 https://github.com/theos/theos/issues/188 关于安全性奴役的纸 http://mista.nu/research/sep-paper.pdf 细数的iOS上的那些安全防护 龙磊,黑雪,蒸米@阿里巴巴移动安全 http://jaq.alibaba.com/community/art/show?spm=a313e.7916642.220000NaN1.1.t8scDb&articleid=486 citizenlab.org1 百万美元的持不同政见者:国家统计局集团的iPhone零日对阿联酋人权捍卫者使用 - 公民实验室 这份报告描述了政府如何定位一个国际公认的人权捍卫者,艾哈迈德·曼苏尔,与三叉戟,零日攻击,旨在感染他的iPhone与先进的商业间谍链。 NavilleZhang https://github.com/Naville 狄奥:统一的跨平台Makefile文件系统 https://github.com/theos/theos/wiki How to dump the content of keychain from the shell? http://apple.stackexchange.com/questions/184897/how-to-dump-the-content-of-keychain-from-the-shell Breaking into the OS X keychain http://juusosalonen.com/post/30923743427/breaking-into-the-os-x-keychain

优秀的个人博客,低调大师

Impala简介(整理

一、定义(来自百度百科) Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据。已有的Hive系统虽然也提供了SQL语义,但由于Hive底层执行使用的是MapReduce引擎,仍然是一个批处理过程,难以满足查询的交互性。相比之下,Impala的最大特点也是最大卖点就是它的快速。 二、Impala组成 1、客户端:包括JDBC、ODBC、Hue、Impala Shell等,用于执行查询或完成管理任务; 2、Hive Metastore:存储可用于Impala数据的信息,包括可用数据库及其结构。当执行Impala Sql语句进行schema对象的创建、修改及删除,或加载数据到表中等操作时,相关元数据的变化,通过单独的catalog服务自动广播到所有Impala节点; 3、Cloudera Impala(Impalad进程):运行于数据节点的Impala程序,用于协调和执行查询。每一个Impala的实例可以获取、解析以及协调Impala客户端传来的查询。查询是被分布到各Impala节点间,这些节点作为workers,并行执行查询片段; 4、HDFS、HBase:数据的实际存储位置。 三、Impala查询执处理过程 1、用户程序通过JDBC、ODBC、Impala Shell等Impala 客户端发送Sql语句给Impala; 2、用户程序连接到集群中任意Impalad进程,这一进程作为整个查询的协调器; 3、Impala解析、分析查询,确定哪些任务由集群中哪一Impalad实例执行,并生成最优执行计划; 4、Impalad实例访问对应HDFS、HBase服务,获取数据; 5、每一个Impalad实例将数据返回给协调器Impalad,由其发送结果给客户端。 四、优点(来自百度百科) 1、Impala不需要把中间结果写入磁盘,省掉了大量的I/O开销。 2、省掉了MapReduce作业启动的开销。MapReduce启动task的速度很慢(默认每个心跳间隔是3秒钟),Impala直接通过相应的服务进程来进行作业调度,速度快了很多。 3、Impala完全抛弃了MapReduce这个不太适合做SQL查询的范式,而是像Dremel一样借鉴了MPP并行数据库的思想另起炉灶,因此可做更多的查询优化,从而省掉不必要的shuffle、sort等开销。 4、通过使用LLVM来统一编译运行时代码,避免了为支持通用编译而带来的不必要开销。 5、用C++实现,做了很多有针对性的硬件优化,例如使用SSE指令。 6、使用了支持Data locality的I/O调度机制,尽可能地将数据和计算分配在同一台机器上进行,减少了网络开销。

优秀的个人博客,低调大师

Java 集合整理大全

HashMap与HashTable的区别 1.HashMap是非线程安全的,HashTable是线程安全的2.HashMap的键和值都可以为null,HashTable则不行3.线程安全问题,所以HashMap的效率比HashTable高 ArrayList和LinkList的比较 ArrayList和LinkedList都不是线程安全的,小并发量的情况下可以使用Vector,若并发量很多,且读多写少可以考虑使用CopyOnWriteArrayList。因为CopyOnWriteArrayList底层使用ReentrantLock锁,比使用synchronized关键字的Vector能更好的处理锁竞争的问题。 TreeSet, LinkedHashSet and HashSet 的区别 TreeSet的主要功能用于排序,它是无序的(插入顺序)LinkedHashSet的主要功能用于保证FIFO即有序的集合(先进先出)HashSet只是通用的存储数据的集合共同点三者都不是线程安全的,如果要使用线程安全可以collections.synchronizedSet()不同点HashSet插入数据最快,其次LinkHashSet,最慢的是TreeSet因为内部实现排序HashSet不保证有序,LinkHashSet保证FIFO即按插入顺序排序,TreeSet安装内部实现排序,也可以自定义排序规则HashSet和LinkHashSet允许存在null数据,但是TreeSet中插入null数据时会报NullPointerException TreeSet有两种排序方式 自然排序 比较器排序 自然排序 public class Student implements Comparable<Student>{ @Override public int compareTo(Student s) { 比较器排序 public class MyComparator implements Comparator<Student> { @Override public int compare(Student s1,Student s2) { TreeMap在添加、删除、定位映射关系上,性能比HashMap要差。适用于有序集合。由于Set集合是唯一性的,由HashSet类实现的Set集合的优点是能够快速定位集合中的元素。HashSet类需要重新实现equals()方法和hashCode()方法。 Queue和Deque接口继承Collection接口,实现FIFO(先进先出)的集合。二者的区别在于,Queue只能在队尾入队,队头出队,而Deque接口则在队头和队尾都可以执行出/入队操作 Queue接口常用方法: add(E)/offer(E):入队,即向队尾追加元素,二者的区别在于如果队列是有界的,add方法在队列已满的情况下会抛出IllegalStateException,而offer方法只会返回falseremove()/poll():出队,即从队头移除1个元素,二者的区别在于如果队列是空的,remove方法会抛出NoSuchElementException,而poll只会返回nullelement()/peek():查看队头元素,二者的区别在于如果队列是空的,element方法会抛出NoSuchElementException,而peek只会返回nullDeque接口常用方法: addFirst(E) / addLast(E) / offerFirst(E) / offerLast(E)removeFirst() / removeLast() / pollFirst() / pollLast()getFirst() / getLast() / peekFirst() / peekLast()removeFirstOccurrence(Object) / removeLastOccurrence(Object)Queue接口的常用实现类: ConcurrentLinkedQueueConcurrentLinkedQueue是基于链表实现的队列,队列中每个Node拥有到下一个Node的引用: private static class Node { volatile E item; volatile Node<E> next; }由于Node类的成员都是volatile的,所以ConcurrentLinkedQueue自然是线程安全的。能够保证入队和出队操作的原子性和一致性,但在遍历和size()操作时只能保证数据的弱一致性。 LinkedBlockingQueue与ConcurrentLinkedQueue不同,LinkedBlocklingQueue是一种无界的阻塞队列。所谓阻塞队列,就是在入队时如果队列已满,线程会被阻塞,直到队列有空间供入队再返回;同时在出队时,如果队列已空,线程也会被阻塞,直到队列中有元素供出队时再返回。LinkedBlocklingQueue同样基于链表实现,其出队和入队操作都会使用ReentrantLock进行加锁。所以本身是线程安全的,但同样的,只能保证入队和出队操作的原子性和一致性,在遍历时只能保证数据的弱一致性。 ArrayBlockingQueueArrayBlockingQueue是一种有界的阻塞队列,基于数组实现。其同步阻塞机制的实现与LinkedBlocklingQueue基本一致,区别仅在于前者的生产和消费使用同一个锁,后者的生产和消费使用分离的两个锁。 ConcurrentLinkedQueue vsLinkedBlocklingQueue vs ArrayBlockingQueue ConcurrentLinkedQueue是非阻塞队列,其他两者为阻塞队列三者都是线程安全的LinkedBlocklingQueue是无界的,适合实现不限长度的队列, ArrayBlockingQueue适合实现定长的队列 SynchronousQueueSynchronousQueue算是JDK实现的队列中比较奇葩的一个,它不能保存任何元素,size永远是0,peek()永远返回null。向其中插入元素的线程会阻塞,直到有另一个线程将这个元素取走,反之从其中取元素的线程也会阻塞,直到有另一个线程插入元素。 这种实现机制非常适合传递性的场景。也就是说如果生产者线程需要及时确认到自己生产的任务已经被消费者线程取走后才能执行后续逻辑的场景下,适合使用SynchronousQueue。 PriorityQueue & PriorityBlockingQueue这两种Queue并不是FIFO队列,而是根据元素的优先级进行排序,保证最小的元素最先出队,也可以在构造队列时传入Comparator实例,这样PriorityQueue就会按照Comparator实例的要求对元素进行排序。 PriorityQueue是非阻塞队列,也不是线程安全的,PriorityBlockingQueue是阻塞队列,同时也是线程安全的。 Deque 的实现类包括LinkedList(前文已描述过)、ConcurrentLinkedDeque、LinkedBlockingDeque,其实现机制与前文所述的ConcurrentLinkedQueue和LinkedBlockingQueue非常类似,此处不再赘述

优秀的个人博客,低调大师

Java面试整理《上》

一、Java基础部分 简单介绍下Java的跨平台原理不同的操作系统的操作指令集不同,所以做程序开发的时候需要根据不同的操作系统开发程序。比如想要程序支持MacOS、Windows和Linux就要开发三套不同的程序代码。这会导致开发成本成倍提升,所以很多程序都只开发了windows版本。这是相对于桌面程序来说的,如果是WEB应用程序就没有这个烦恼,这也是WEB开发这么流行的原因。Sun公司开发了适用于不同操作系统及位数的JVM虚拟机来屏蔽系统之间的差异并提供了统一的接口,对于Java程序而言,只需要遵循Java语言规范,就可以在所有的操作系统上运行程序。 Java中int数据占用几个字节?int数据占用4个字节,这在哪个位数操作系统上都是一样的,主要是为了跟CPU的字长一致,目的当然是提高处理速度。 Java面向对象有哪些特征?主要有四个特征:抽象,封装,继承和多态。 抽象的目的是忽略与主题无关的其他信息,使用abstract关键字修饰,规定抽象方法只能为public/protected,被修饰的类不能被实例化,只允许通过继承的方式来实现。 封装的意思就是想让你看到就让你看到不想让你看到就不让你看到,使用private关键字实现,当你需要对属性进行修改的时候就使用类中的get/set方法,使用public关键字。这是出于安全和易用性方面的考虑。 继承的意义在于不需要重复造轮子,子类继承父类就可以得到除了private之外的父类的全部属性和方法(父类的构造方法除外)。需要知道的是Java只支持单继承,这是出于对继承链可能会出现过于复杂的情况的设计方案,如果想实现类似C++语言的多继承,可以使用接口来实现。还需要注意这几点,如果没有指定父类,那么类的直接父类是java.lang.Object;一般继承会导致重写,子类有时候会需要重写父类的方法,在重写时必须保证方法名和形参列表相同,返回值类型和异常类型子类需要小于父类;访问权限,子类需要大于父类。 多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用只有在程序运行期间才会确定,在Java中通过继承的方式来实现多态,即父类接口引用变量指向子类或实现类的实例对象,这样程序调用的方法在运行期才会动态绑定引用变量所指向的具体实例对象的方法,也就是内存里正在运行的对象方法。。比如公司规定十点上班工作,但并不会对程序员说你要敲代码,并不会对HR说你要检查邮件这个工作的具体实现。需要注意的是多态是方法的多态而不是属性的多态,多态是在继承的基础上实现的。 接口和抽象的区别是什么?两者都是Java面向对象的重要实现。它们都声明方法而不去实现,本质的区别在于抽象类是一个类,而接口是其他的类型,在OC中都没有接口这一说,而是使用protocol。抽象类除了不能实例化之外跟普通的类没有区别,而接口完全不存在方法的实现,不能有构造器,只能使用public访问修饰符,没有main方法等。接口提供抽象方法,类通过interface关键字对接接口,重写抽象方法实现对应业务逻辑以此对外提供服务。比如Map接扣作为一个容器就会提供put方法来往容器中填充值,get方法获取容器中某一个值,size属性获取容器大小,remove方法来删除容器中的值。接口除了做规范之外还是多继承在Java中的解决方案,另外接口也是各种框架和API的实现,比如RedisTemplate就是封装了jedis,如果在项目中使用redis,直接通过注入RedisTemplate接口就可以完成对redis的操作。 已经有基本类型了,为什么还需要包装类型呢?Java提供了八种基本类型,每一种类型都会有一个对应的包装类型,比如boolean-Boolean,JVM会在两者之间自动转换,这是JDK1.5做的改变。 Integer i = 1; //自动装箱,编译时会调用Integer.valueOf(1) int j = i; //自动拆箱,编译时调用intValue(); 值得注意的是自动装箱时系统会创建缓存,并将当前数据添加到缓存中,有两个作用,第一是为了缓解使用包装类型导致数据存储会略微变慢的问题,第二是解决了包装类型判断是否相等的问题。 Java是一门面向对象的语言,基本数据类型并不具备面向对象的特征,这会导致数据存储的问题,例如在处理空值的时候,使用Integer时需要判断null,使用int的时候需要判断0,而在数据库中通常默认值为0。这会导致在数据库中存储id的时候,id类型为bigint,当控制类方法的接收值为long而不是Long的话会报错。使用Long表示类的ID的时候,在需要判断类是否存在只需要判断为null就可以了,而且使用包装类型就可以通过其提供的方法MAX和MIN获取最大值和最小值等。 说一下"=="和equals方法究竟有什么区别?==用于判断两个变量是否相等。变量可以分为基本数据类型和引用数据类型,如果是引用数据类型需要比较引用的内存首地址,如果是基本数据类型直接比较值。 equals方法是判断两个对象的某些特征是否一致,通过重写对象的equals方法。 如何理解?通过观察源码会发现,equals方法会首先判断两个比较值类型是否一致,然后再比较数值或字符,例如Integer类型会自动拆箱为int类型进行比较,String会将字符串拆分成字符。 讲一下String和StringBuilder的区别(final)?StringBuffer和StringBuilder的区别?java提供了三个类String、StringBuilder和StringBuffer来表示和操作字符串。字符串即是由多个字符(char)的集合。 String是内容不可变的字符串(private final char value[]) StringBuilder和StringBuffer继承自AbstractStringBuilder类(char[] value); 因此拼接字符串的时候: String:String s = "a"+"b"; StringBuilder sb = new StringBuilder(); sb.apend("a").apend("c") 拼接字符串的时候不能使用String进行拼接,要使用StringBuilder和StringBuffer。前者效率高但线程不安全,后者效率低但线程安全。 简单说一下java中的集合 java中的集合主要分为value和key-value两种。 存储值分类list和set,list重复有序,set不可重复无序。并且set根据equals和hashcode判断,如果对象存储在set中,则必须重写这两个方法。 存储键值在java中是称之为map,在OC语言中叫字典。 List常用的ArrayList和LinkedList的区别和使用场景? ArrayList底层使用数组(private transient Object[] elementData;) LinkedList底层使用链表(transient Node<E> first; transient Node<E> last;) 数组是一块连续的内存,因此插入和删除的时候需要移动内存。由于有这个特点所以说数组查询元素快,插入和删除删除慢。 链表的原理是在当前元素中存放上一个和下一个元素的地址,查询时从头部开始一个个找,因此查询效率慢,由于做修改的时候不需要做内存移动操作,只需要改变引用指向即可完成,所以插入和删除操作效率高。 不难看出链表和数组是相反的,所以它们的使用场景就呼之欲出了,而由于数据库操作的特殊性(查询占90),所以一般我们会选择ArrayList做数据保存的容器。 讲一下HashMap和 HashTable的区别?HashTable和ConcurrentHashMap的区别? 首先HashTable是不可以使用null作为key或者value的,HashTable在存储数据的时候会返回synchronized。因此可以得出结论HashTable安全但效率不高。 如果想既效率高又安全,可以使用ConcurrentHashMap并发容器的方式,原理是将整个Map分成N个Segment(类似HashTable不加锁),提供相同的线程安全,但是效率提升N倍默认为16倍。 实现一个拷贝文件的工具类使用字节流还是字符流? 字节流和字符流都可以实现对文件的拷贝,如果确认不包含例如图片和音频这样特殊的文件,使用字符流即可,否则应该使用字节流。 线程的几种实现方式,启动方式? 两种实现方式:继承Thread类和实现Runnable接口。一般采用实现接口的方式,因为java只支持单继承。 Thread thread = new Thread(new MyThread());//MyThread类继承了Thread对象或Runnable接口 thread.setName("xiaoming");//通过设置线程名称来区分线程 thread.start();//通过start()方法来启动线程,但实际执行的方法是run方法。run()方法是Thread类和Runnable接口自带的方法,需要重写和实现run()。 线程池的作用 线程过多的话会导致系统运行缓慢甚至崩溃,所以需要限制线程的个数 线程池如果每次都创建或者销毁会导致资源的浪费 什么是设计模式?常用的设计模式有哪些? 设计模式是前人经验所得的可以反复使用,解决特定问题的设计方法。 单例模式是最常见的设计模式之一,它的作用是保证某一实例的唯一性。具体实现就是在类中定义一个实例对象并提供一个公开方法获取该实例,然后将构造器私有化,如果需要使用该实例的话,只能通过提供的公开方法而不能new出这个类,这样每次得到的实例就是同一个实例。单例分为懒加载和立即加载两种模式,懒加载在创建对象的时候就加载实例,立即加载在需要的时候才加载实例。 工厂模式,SpringIoC中的BeanFactory,MyBatis中的SqlSessionFactory就都是工厂模式的实现 代理模式,SpringAOP是通过动态代理实现 观察者模式,Spring中的listener实现ApplicationListener 模板模式,Spring集成了JdbcTemplate、RedisTemplate、SolrTemplate、JmsTemplate 适配器模式,SpringMVC中的处理器映射器HandlerMapper 二、J2EE部分 说一下你对servlet的理解?或者servlet是什么?Java Servlet是使用J2EE服务端程序,后端代码通过实现Servlet接口并重写HTttpServlet的doGet/doPost方法来进行对数据库和浏览器之间的交流,Servlet程序需要运行在支持Java程序的应用服务器中,比如tomcat,jetty等。 简单说一下servlet的生命周期?Servlet是一个接口规范,所以有比较明确的生存期定义,接口中的init,service和destory方法进行表述。 Servlet API中forward()与redirect()的区别?forward就是转发,是服务端行为因此只发送一次请求也不会改变url地址,redirect是重定向,是客户端行为会改变url地址并且发送两次请求 JSP和Servlet有哪些相同点和不同点?JSP是Servlet的扩展,所有的jsp文件都会被编译成一个继承自HttpServlet的类,因为servlet输出html比较麻烦,所以设计出jsp。 JSP有哪些内置对象?作用分别是什么? 九个内置的对象: request 用户端请求,此请求会包含来自GET/POST请求的参数 response 网页传回用户端的回应 pageContext 网页的属性是在这里管理 session 与请求有关的会话期 application servlet正在执行的内容 out 用来传送回应的输出 config servlet的构架部件 page JSP网页本身 exception 针对错误网页,未捕捉的例外 四大作用域: pageContext 整个page页面 request(HttpServletRequest) service方法 session(HttpSession) 第一次调用request.getSession()方法时 application(ServletContext) 整个WEB应用程序 说一下Session和Cookie的区别?你在项目中都有哪些地方使用了?session和cookie都是HTTP的会话跟踪技术,是为了解决HTTP无状态的特性而设计的技术。cookie在客户端浏览器记录信息认证身份,session在服务端记录信息确认身份。session通过在cookie中存放的sessionId来保证信息的一致性。 session和cookie的区别如下 由于cookie数据保存在浏览器中,因此有安全和数据限制的问题,考虑到这种情况应该使用session保存数据 session数据存在服务器上,当访问比较多的情况下会耗费服务器资源,考虑到这种情况应该使用cookie购物车是最好的cookie技术实现场景,用户登录信息的存储是最好的session技术实现场景。 我在做用户注册生成短信验证码和solr异步删除索引库数据,静态页面生成的时候使用过jmsTemplate.send()方法,该方法中有一个内部类(new MessageCreator()),此类中接收session参数,然后通过这个session创建消息实例设置消息并返回。 HTTP中GET和POST请求的区别用户通过不同的请求方式完成对资源的不同操作,比如PUT请求就是代表对请求资源的增加,DELETE请求就是对请求资源的删除。为了方便开发,一般我们只使用GET和POST方式就可以了。两者本质上都是TCP链接,区别在于: get请求时,地址栏会显示请求信息,以?开头,多个参数以&连接,而post请求在request body中 get请求由于浏览器对地址长度的限制而导致传输的数据有限,post则没有这个限制 由于请求信息会暴露所以get请求会有安全隐患 get产生一个TCP数据包,浏览器会将http header和data数据一起发送,等待服务器响应200并返回数据;post请求产生两个数据包,浏览器会发送header,等待服务器响应100 continue,浏览器才会发送data,最后等待服务器响应200和请求的数据 对于get和post的区别,有一个比较有趣的比喻。get传递数据如同写在脸上,post传递数据如同方法肚子里,脸上自然不能放太多东西也不能放隐私的东西,而肚子里就无所谓了。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册