首页 文章 精选 留言 我的

精选列表

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

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面试整理《下》

一、数据库部分 简单介绍下关系数据库三范式 范式指的是数据库设计时要遵循的三个规范,但并不是强制式的,这三个规范呈阶梯式的关系,现有第一层然后在第一层的基础上有第二层... 第一范式规定列数据不可分割,即实体中的某个属性不能有多个值或者重复的属性;第二范式要求每个行必须可以被唯一的区分,就是表中必须有主键存在的意思;第三范式规定表中不包含已在其他表中已包含的非主关键字信息,意思是除了外键之外不能存储其他表的数据信息。 反三范式:出于对效率和便利性的追求,可以违反三范式设置重复或可以推导出的字段,比如为了在查询订单的总价时可以不用去计算每个订单项中的单价和在订单表中设置订单总价字段。 可以简单介绍下什么是事务? 事务是并发控制的最小单位(需要满足ACID),用户定义的一个操作序列要么都执行成功,要么都执行失败,比如转账,A扣钱,B加钱,这两个操作必须是不可分割的,在commit之后也不能被干扰,将执行的代码try/catch并调用回滚rollback撤销当前事务操作。 mysql数据库的默认的最大连接数? mysql数据的默认连接数大小是100,数据库在安装时都会设置一个最大连接数,这是因为一般服务器都只能支持一定数目同时连接。 说一下msyql的分页?oracle的分页? oracle的分页是采用了三层嵌套查询,没有用过不赘言。 mysql是使用关键字limit来进行分页查询的 String sql ="select * from students order by id limit " + pageSize*(pageNumber-1) + "," + pageSize; 存储过程是什么?它有什么优缺点? 存储过程本质上是sql语句的集合,这些集合像一个方法一样实现某一特定的功能,通过设置名称的方式提供调用。 由于是预编译的代码块,所以执行效率会比较高;由于很多SQL一起执行,所以节省网络带宽;存储过程提供安全机制,可以对于没有权限执行存储过程的用户进行控制; 缺点则是调试和更改时比较麻烦,而且也没办法做缓存和做数据库集群。因此对于存储过程的使用要慎重,对于提高SQL查询的性能而言,ORM框架做的会比存储过程好。 SQL语言本质是一种结构化查询语言,复杂的业务逻辑还是应该通过代码去实现。 有没有做过数据库优化方面的事情? 主要是使用索引、分表和缓存来做数据库优化,当有多个数据库服务器时,还可以采用读写分离的方式做数据库集群。 数据库优化之创建合适的索引 普通索引:允许重复的值出现 唯一索引:除了不能重复记录之外,其它的和普通索引一样,例如用户名,身份证,email,tel 主键索引:将表中某列设置为主键的时候数据库引擎会自动将该列设置为主键索引,这个主键索引唯一并且没有不能为null 全文索引:主要针对表中的文本域进行索引,比如char,varchar,text等字段 索引的弊端一是占用磁盘空间,二是对DML(增删改)操作会降低效率。 索引配合where条件,一般在字段内容不是频繁变化的时候使用,并且该字段的内容不是唯一的几个值,例如性别这个字段只有男女或未知 索引使用的技巧:对于like查询,如果模糊信息为'%aaa'的话就不会使用索引,因此在like查询中,关键字的最前面最好不要使用'%'或者'_'这样的字符,如果前面的值一定要有变化,则考虑使用全文索引(sphinx);如果条件查询中带有'or'关键字就不会使用索引,所以要避免使用'or'关键字;如果列是字符串类型,无论是不是字符串数字都要使用''包括起来,否则当值是数值类型的话会自动转换,如果不是数值类型则会报错;当表中只有一条数据时,全表扫描要比索引快,所以为了"效率"的查询数据,应该使用全表扫描的方式,233... 数据库优化之分表 当数据库单个表中数据越来越大(百万级别)的时候会导致数据查询缓慢,又因为数据库是多用户共享资源,并发存取同一数据的时候时需要添加锁机制来控制用户操作,所以会出现数据库性能瓶颈。这个时候就需要对数据库进行分表,就是把数据量大的表分成多个数据表,比如商品SPU和SKU表的商品描述信息进行表抽取和对单个数据比如价格,0-100和100-200在不同表中或通过自增长ID切割,这是水平分表。比如在电商项目中类似商品的分类、创建时间、品牌和规格等信息具有变化频率慢,查询次数多,而且有很好的实时性的数据,把这样的数据称之为冷数据;类似商品评论,广告,销量和价格排序这些变化频率慢的数据称之为热数据。将这些数据通过更改存储引擎,使用memcache、redis和从库配置等进行不同的处理。 数据库优化之读写分离 一台数据库服务器所支持的最大并发连接数是有限的,如果用户并发访问太多就需要考虑搭建集群,读写分离就是mysql处理集群的相关技术。使用负载均衡来实现写操作往主数据库库中去,读操作往从数据库中去。数据库最终都会把数据库持久化到磁盘,因此集群必须保证每个数据库服务器中数据是一致的,这就是主从同步技术。 数据库优化之缓存 每次读取数据都需要操作数据库,这对数据库很不友好,所以需要在持久层DAO和数据库DB中添加一个缓存层,这个缓存层一般都使用内存实现,如果访问的数据能够从缓存服务器中读取,那么就不会访问数据库。在Java中的缓存有ORM框架的二级缓存,需要注意的是Hibernate的二级缓存不能完成分布式缓存,还有可以使用memcache或者redis来对缓存的数据进行集中处理。 二、前端部分 简单说一下html,css,javascript在网页开发中的定位? HTML 超文本标记语言 定义网页的结构 CSS 层叠样式表,用来美化页面 JavaScript主要用来验证表单,做动态交互(其中ajax) 简单介绍一下Ajax? 如果不使用ajax,拿直播页面来说,你点击直播间的刷新按钮,那么包括弹幕在内的整个网页都会刷新。所以ajax不只是一门技术,只要是通过js调用异步通讯组件并使用格式化数据来更新页面上的内容就可以说是ajax,它实现了页面中局部位置的定点刷新。 ajax全称AsynchronousJavascript+XML(异步传输+js+xml),所谓异步就是在向服务器发送请求的时候不需要等待结果就可以做其他事,这就代表你可以在一个页面上同时看直播视频界面和弹幕。现在一般使用json来代替xml,好处是json更加简洁,解析速度也更快。 ajax原理是通过创建XmlHttpRequest对象,XmlHttpRequest是js提供的一个为客户端提供客户端与服务器之间传输数据的API对象。通过这个API对象调用onreadystatechange事件,该事件用于获取客户端浏览器向服务器发送数据的状态(200,404等),得到状态之后就会调用编写的回调函数。在这个过程中,其他代码还是在不断接收数据解析数据的,只是不会阻塞js代码。 var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { //存储函数 当readyState属性发生改变时就会调用 Fuck(xhr.readyState);//readyState属性 0=请求未初始化,1=服务器连接已建立,2=请求已连接,3=请求处理中,4=请求已完成,且响应就绪 Fuck(xhr.status);//status属性 200=ok,404=访问资源未找到,401=没有访问权限,402=预留的状态码,403=禁止访问,500=服务器内部错误等等 } 缺点:ajax破坏了浏览器的back和history机制;违反了URL资源定位设计的初衷;对搜索引擎(SEO)有影响;不能很好的支持移动设备;破坏了程序的异常处理机制;由于现在的程序将大量原先在后端的代码放到了js文件中会导致客户端程序复杂并存在安全隐患。 使用场景:登录失败时不跳转页面,注册时提示用户名是否存在,二级联动。 js和jQuery的关系? JQ是一个封装了js属性和方法的框架,可以让开发者使用便利的同时也增强了js的功能,如果是使用原生js的话就需要处理很多兼容性问题和事件绑定,dom操作,ajax,注册事件等。JQ常用的选择器有ID选择器,class选择器,标签选择器等。 jQuery的页面加载完毕事件? 在js中想要获取元素必须要先加载元素,可以将获取元素的js代码放到元素标签后,但是这样会造成管理上的不便,所以一般等页面全部加载之后在获取元素。一般使用$(funcation(){});//当页面加载完毕之后会执行花括号中的函数,这是相对简单的方式,因此使用也是最多的。 需要注意的是JQ中的页面加载是指页面结构被加载完毕,而window.onload表示的是页面被加载完毕。 <img src=”htttp://baidu.com/1.jpg”/> onload必须等页面中的图片、声音、图像等远程资源被加载完毕后才调用,而jQuery中只需要页面结构被加载完毕。 简单说一下html5和css3?你对现在的那些新技术有了解? html5是最新版本的html,在html4的基础上增强了一些标签,比如画板,声音,视频,web存储这些功能。但是html5过于强调语义,比如使用header标签表示头部,footer表示底部。以前在开发都是采用div标签定义模块。 css3在css2的基础上做了增强,实现了一些原来在css2中实现起来比较困难,或者不能实现的功能,比如盒子和文字阴影,渐变,动画等。css3的缺点在于需要根据不同的浏览器处理兼容性,其实html5也有兼容性问题,比如html5播放器panda.tv就出于兼容性的考虑没有使用html5播放器。 三、框架部分 什么是框架? it语境中的框架是指为了解决某一开放性问题而设计的具有一定约束性的支撑结构,在这种结构上可以根据具体问题进行扩展,从而便捷的构建完整的解决问题的方案。 框架不能完全解决实际上遇到的问题,但可以快速的解决普遍问题。 框架是为了扩展而设计的 一般框架会提供很多辅助性的实用工具,比如java的一系列jar包就是对jdk功能的扩展。 简单讲一下SpringMVC的执行流程? SpringMVC本质上是一个前端控制器(DispatcherServlet),通过对处理器映射器,处理器适配器和视图解析器的调度来实现接收请求和响应数据。 说一下struts2和springMVC有什么不同? MVC的核心是Servlet,Strtus2的核心是Filter;MVC一般使用注解开发,Strtus2一般使用xml配置;MVC处理ajax请求,直接返回数据,在方法中通过注解@RequestBody,MVC就会自动将对象转换成json数据;而strtus2是通过插件转换的。 说一下Spring中的两大核心? IoC(Inversion of Control)或者称之为DI(Dependency Injection)是指程序将DAO的创建权交付Spring管理,通过配置文件和反射再加上Map来实现自动创建Bean。 AOP(Aspect Oriented Programming)被称之为面向切面编程,使用动态代理的方式在执行方法前后或应用程序出现异常时加入相关逻辑。比如对于事务和日志的处理。 什么是ORM? 对象关系映射简称ORM(Object Relational Mapping),通过对数据和对象之间映射元数据的方式,将程序中的对象自动持久化到关系数据库。 iBatis(mybatis)与Hibernate有什么不同? mybatis的好处在于将sql语句与java代码分离并提供将结果集自动封装为实体对象和对象集合的功能,还提供了自动将实体对象的属性传递给sql语句的参数这样的功能。 hibernate的好处在于可以自动生成sql语句并执行同时返回java结果。 两者最大的不同在于mybatis是面向sql的所以需要在xml配置文件中写sql,而hibernate是自动生成sql,需要考虑对象之间复杂的映射关系。因为这个特性所以hibernate无法完成特别复杂的查询。 四、其他部分 有没有用过linux?你都用它来做什么?linux的特点是长时间运行比较稳定,所以一般会用做服务器。linux提供C语言编译环境,所以需要C语言支持的redis和nginx等可以通过在C语言编译环境中获取软件包并运行。 说一下linux下面的一下常用命令? 查看文件 tail -f 删除文件 rm -rf 编辑文件 vi 创建文件夹 mkdir 删除文件夹 rm -f 获取当前路径 pwd 跳转目录 cd 切换用户 su -root 列举目录 ls 你是使用什么来连接远程的Linux服务器的? 需要依赖于Linux服务器安装ssh服务端,一般这个ssh服务的端口22. 需要依赖于Linux服务器安装sftp服务端,一般这个sftp服务的端口25. 使用xshell、putty等ssh客户端来连接服务器,使用xftp、winscp等sftp客户端来上传和现在文件。连接和上传、下载必须依赖于服务器的ssh、sftp服务,也就是linux服务器需要启动这两个服务。 没有使用过云主机?客户有租用过阿里的云主机,云主机就是一些云服务运营商比如阿里,腾讯,华为等提供远程的服务器功能,开发者只需要按需付费即可租用。使用ssh和sftp操作 有没有使用过redis? redis是什么redis是一个key-value的nosql的数据库,redis会先将数据存储到内存中,然后根据一定的策略持久化到磁盘,已达到断电也不会丢失数据。主要用来做缓存数据库的数据和做web集群的时候当做中央缓存存放session。 redis的数据淘汰机制在 redis 中,允许用户设置最大使用内存大小 server.maxmemory,在内存限定的情况下是很有用的。譬如,在一台 8G 机子上部署了 4 个 redis 服务点,每一个服务点分配 1.5G 的内存大小,减少内存紧张的情况,由此获取更为稳健的服务。 内存大小有限,需要保存有效的数据?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(驱逐):禁止驱逐数据 微信开发原理微信公众平台开发者,通过接入认证的方式,让我们的服务器能处理来自微信服务器转发的微信用户的请求,处理完成后返回给微信服务器,有微信服务器对用户响应。 怎么把微信和业务平台绑定?用户注册实体中包含一个微信号的字段,进行绑定操作的时候就是修改这个微信号的字段,我们需要通过微信网页授权的方式获取微信号。 当用户同意授权的时候我们可以得到一个code,通过这个code换取网页授权的微信号,就是openid,最后将openid存入到微信号字段中即可

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

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等操作系统。